Skip to main content

zngur_parser/
lib.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt::Display,
4    path::Component,
5};
6
7#[cfg(not(test))]
8use std::process::exit;
9
10use ariadne::{Color, Label, Report, ReportKind, sources};
11use chumsky::{input::MapExtra, prelude::*};
12use itertools::{Either, Itertools};
13
14use zngur_def::{
15    AdditionalIncludes, ConvertPanicToException, CppRef, CppStackOwned, CppValue, Import,
16    LayoutPolicy, Merge, MergeFailure, ModuleImport, Mutability, PrimitiveRustType,
17    RustPathAndGenerics, RustTrait, RustType, TypeVar, ZngurConstructor, ZngurExternCppFn,
18    ZngurExternCppImpl, ZngurField, ZngurFn, ZngurMethod, ZngurMethodDetails, ZngurMethodReceiver,
19    ZngurSpec, ZngurTrait, ZngurType, ZngurVariant, ZngurWellknownTrait,
20};
21
22pub type Span = SimpleSpan<usize>;
23
24/// Result of parsing a .zng file, containing both the spec and the list of all processed files.
25#[derive(Debug)]
26pub struct ParseResult {
27    /// The parsed Zngur specification
28    pub spec: ZngurSpec,
29    /// All .zng files that were processed (main file + transitive imports)
30    pub processed_files: Vec<std::path::PathBuf>,
31}
32
33#[cfg(test)]
34mod tests;
35
36pub mod cfg;
37mod conditional;
38mod template_types;
39
40use crate::{
41    cfg::{CfgConditional, RustCfgProvider},
42    conditional::{Condition, ConditionalItem, NItems, conditional_item},
43    template_types::try_match_template,
44};
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct Spanned<T> {
48    inner: T,
49    span: Span,
50}
51
52type ParserInput<'a> = chumsky::input::MappedInput<
53    Token<'a>,
54    Span,
55    &'a [(Token<'a>, Span)],
56    Box<
57        dyn for<'x> Fn(
58            &'x (Token<'_>, chumsky::span::SimpleSpan),
59        ) -> (&'x Token<'x>, &'x SimpleSpan),
60    >,
61>;
62
63#[derive(Default)]
64pub struct UnstableFeatures {
65    pub cfg_match: bool,
66    pub cfg_if: bool,
67    pub template_types: bool,
68}
69
70#[derive(Default)]
71pub struct ZngParserState {
72    pub unstable_features: UnstableFeatures,
73}
74
75type ZngParserExtra<'a> =
76    extra::Full<Rich<'a, Token<'a>, Span>, extra::SimpleState<ZngParserState>, ()>;
77
78type BoxedZngParser<'a, Item> = chumsky::Boxed<'a, 'a, ParserInput<'a>, Item, ZngParserExtra<'a>>;
79
80/// Effective trait alias for verbose chumsky Parser Trait
81pub(crate) trait ZngParser<'a, Item>:
82    Parser<'a, ParserInput<'a>, Item, ZngParserExtra<'a>> + Clone
83{
84}
85impl<'a, T, Item> ZngParser<'a, Item> for T where
86    T: Parser<'a, ParserInput<'a>, Item, ZngParserExtra<'a>> + Clone
87{
88}
89
90#[derive(Debug)]
91pub struct ParsedZngFile<'a>(Vec<ParsedItem<'a>>);
92
93#[derive(Debug)]
94pub struct ProcessedZngFile<'a> {
95    aliases: Vec<ParsedAlias<'a>>,
96    items: Vec<ProcessedItem<'a>>,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
100enum ParsedPathStart {
101    Absolute,
102    Relative,
103    Crate,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
107struct ParsedPath<'a> {
108    start: ParsedPathStart,
109    segments: Vec<&'a str>,
110    span: Span,
111}
112
113#[derive(Debug, Clone)]
114struct Scope<'a> {
115    aliases: Vec<ParsedAlias<'a>>,
116    base: Vec<String>,
117    type_vars: HashSet<ParsedTypeVar<'a>>,
118}
119
120impl<'a> Scope<'a> {
121    /// Create a new root scope containing the specified aliases.
122    fn new_root(aliases: Vec<ParsedAlias<'a>>) -> Scope<'a> {
123        Scope {
124            aliases,
125            base: Default::default(),
126            type_vars: Default::default(),
127        }
128    }
129
130    /// Resolve a path according to the current scope.
131    fn resolve_path(&self, path: ParsedPath<'a>) -> Vec<String> {
132        // Check to see if the path refers to an alias:
133        if let Some(expanded_alias) = self
134            .aliases
135            .iter()
136            .find_map(|alias| alias.expand(&path, &self.base))
137        {
138            expanded_alias
139        } else {
140            path.to_zngur(&self.base)
141        }
142    }
143
144    /// Create a fully-qualified path relative to this scope's base path.
145    fn simple_relative_path(&self, relative_item_name: &str) -> Vec<String> {
146        self.base
147            .iter()
148            .cloned()
149            .chain(Some(relative_item_name.to_string()))
150            .collect()
151    }
152
153    fn sub_scope(&self, new_aliases: &[ParsedAlias<'a>], nested_path: ParsedPath<'a>) -> Scope<'_> {
154        let base = nested_path.to_zngur(&self.base);
155        let mut mod_aliases = new_aliases.to_vec();
156        mod_aliases.extend_from_slice(&self.aliases);
157
158        Scope {
159            aliases: mod_aliases,
160            base,
161            type_vars: self.type_vars.clone(),
162        }
163    }
164
165    fn with_type_vars(&self, type_vars: HashSet<ParsedTypeVar<'a>>) -> Scope<'_> {
166        Scope {
167            aliases: self.aliases.clone(),
168            base: self.base.clone(),
169            type_vars,
170        }
171    }
172
173    fn as_type_var(&self, ty: &ParsedRustPathAndGenerics<'a>) -> Option<TypeVar> {
174        if let ParsedRustPathAndGenerics {
175            path:
176                ParsedPath {
177                    start: ParsedPathStart::Relative,
178                    segments,
179                    span: _,
180                },
181            generics,
182            named_generics,
183        } = ty
184            && generics.is_empty()
185            && named_generics.is_empty()
186            && let &[single_elem] = segments.as_slice()
187            && self.type_vars.contains(&ParsedTypeVar(single_elem))
188        {
189            Some(TypeVar(single_elem.to_owned()))
190        } else {
191            None
192        }
193    }
194}
195
196impl ParsedPath<'_> {
197    fn to_zngur(self, base: &[String]) -> Vec<String> {
198        match self.start {
199            ParsedPathStart::Absolute => self.segments.into_iter().map(|x| x.to_owned()).collect(),
200            ParsedPathStart::Relative => base
201                .iter()
202                .map(|x| x.as_str())
203                .chain(self.segments)
204                .map(|x| x.to_owned())
205                .collect(),
206            ParsedPathStart::Crate => ["crate"]
207                .into_iter()
208                .chain(self.segments)
209                .map(|x| x.to_owned())
210                .collect(),
211        }
212    }
213
214    fn matches_alias(&self, alias: &ParsedAlias<'_>) -> bool {
215        match self.start {
216            ParsedPathStart::Absolute | ParsedPathStart::Crate => false,
217            ParsedPathStart::Relative => self
218                .segments
219                .first()
220                .is_some_and(|part| *part == alias.name),
221        }
222    }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct ParsedAlias<'a> {
227    name: &'a str,
228    path: ParsedPath<'a>,
229    span: Span,
230}
231
232impl ParsedAlias<'_> {
233    fn expand(&self, path: &ParsedPath<'_>, base: &[String]) -> Option<Vec<String>> {
234        if path.matches_alias(self) {
235            match self.path.start {
236                ParsedPathStart::Absolute => Some(
237                    self.path
238                        .segments
239                        .iter()
240                        .chain(path.segments.iter().skip(1))
241                        .map(|seg| (*seg).to_owned())
242                        .collect(),
243                ),
244                ParsedPathStart::Crate => Some(
245                    ["crate"]
246                        .into_iter()
247                        .chain(self.path.segments.iter().cloned())
248                        .chain(path.segments.iter().skip(1).cloned())
249                        .map(|seg| (*seg).to_owned())
250                        .collect(),
251                ),
252                ParsedPathStart::Relative => Some(
253                    base.iter()
254                        .map(|x| x.as_str())
255                        .chain(self.path.segments.iter().cloned())
256                        .chain(path.segments.iter().skip(1).cloned())
257                        .map(|seg| (*seg).to_owned())
258                        .collect(),
259                ),
260            }
261        } else {
262            None
263        }
264    }
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
268struct ParsedImportPath {
269    path: std::path::PathBuf,
270    span: Span,
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
274enum ParsedItem<'a> {
275    ConvertPanicToException(Span),
276    CppAdditionalInclude(&'a str),
277    UnstableFeature(&'a str),
278    Mod {
279        path: ParsedPath<'a>,
280        items: Vec<ParsedItem<'a>>,
281    },
282    Type {
283        ty: Spanned<ParsedRustType<'a>>,
284        items: Vec<Spanned<ParsedTypeItem<'a>>>,
285        type_vars: Option<HashSet<ParsedTypeVar<'a>>>,
286    },
287    Trait {
288        tr: Spanned<ParsedRustTrait<'a>>,
289        methods: Vec<ParsedMethod<'a>>,
290    },
291    Fn(Spanned<ParsedMethod<'a>>),
292    ExternCpp(Vec<ParsedExternCppItem<'a>>),
293    Alias(ParsedAlias<'a>),
294    Import(ParsedImportPath),
295    ModuleImport {
296        path: std::path::PathBuf,
297        span: Span,
298    },
299    MatchOnCfg(Condition<CfgConditional<'a>, ParsedItem<'a>, NItems>),
300}
301
302#[derive(Debug, Clone, PartialEq, Eq)]
303enum ProcessedItem<'a> {
304    ConvertPanicToException(Span),
305    CppAdditionalInclude(&'a str),
306    Mod {
307        path: ParsedPath<'a>,
308        items: Vec<ProcessedItem<'a>>,
309        aliases: Vec<ParsedAlias<'a>>,
310    },
311    Type {
312        ty: Spanned<ParsedRustType<'a>>,
313        items: Vec<Spanned<ParsedTypeItem<'a>>>,
314        type_vars: Option<HashSet<ParsedTypeVar<'a>>>,
315    },
316    Trait {
317        tr: Spanned<ParsedRustTrait<'a>>,
318        methods: Vec<ParsedMethod<'a>>,
319    },
320    Fn(Spanned<ParsedMethod<'a>>),
321    ExternCpp(Vec<ParsedExternCppItem<'a>>),
322    Import(ParsedImportPath),
323    ModuleImport {
324        path: std::path::PathBuf,
325        span: Span,
326    },
327}
328
329#[derive(Debug, Clone, PartialEq, Eq)]
330enum ParsedExternCppItem<'a> {
331    Function {
332        is_safe: bool,
333        method: Spanned<ParsedMethod<'a>>,
334    },
335    Impl {
336        tr: Option<ParsedRustTrait<'a>>,
337        ty: Spanned<ParsedRustType<'a>>,
338        methods: Vec<(bool, ParsedMethod<'a>)>,
339    },
340}
341
342#[derive(Debug, Clone, PartialEq, Eq)]
343enum ParsedConstructorArgs<'a> {
344    Unit,
345    Tuple(Vec<ParsedRustType<'a>>),
346    Named(Vec<(&'a str, ParsedRustType<'a>)>),
347}
348
349#[derive(Debug, Clone, PartialEq, Eq)]
350enum ParsedLayoutPolicy<'a> {
351    StackAllocated(Vec<(Spanned<&'a str>, usize)>),
352    Conservative(Vec<(Spanned<&'a str>, usize)>),
353    HeapAllocated,
354    OnlyByRef,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq)]
358enum ParsedTypeItem<'a> {
359    Layout(Span, ParsedLayoutPolicy<'a>),
360    Traits(Vec<Spanned<ZngurWellknownTrait>>),
361    NonExhaustive,
362    Constructor {
363        args: ParsedConstructorArgs<'a>,
364    },
365    Variant {
366        name: &'a str,
367        items: Vec<Spanned<ParsedTypeItem<'a>>>,
368    },
369    Field {
370        name: String,
371        ty: ParsedRustType<'a>,
372        offset: Option<usize>,
373    },
374    Method {
375        data: ParsedMethod<'a>,
376        use_path: Option<ParsedPath<'a>>,
377        deref: Option<ParsedRustType<'a>>,
378        cpp_name: Option<&'a str>,
379    },
380    CppValue {
381        field: &'a str,
382        cpp_type: &'a str,
383    },
384    CppRef {
385        cpp_type: &'a str,
386    },
387    CppStackOwned {
388        cpp_type: &'a str,
389        props: Vec<(Spanned<&'a str>, usize)>,
390    },
391    MatchOnCfg(Condition<CfgConditional<'a>, ParsedTypeItem<'a>, NItems>),
392}
393
394#[derive(Debug, Clone, PartialEq, Eq, Hash)]
395struct ParsedTypeVar<'a>(&'a str);
396
397#[derive(Debug, Clone, PartialEq, Eq)]
398struct ParsedMethod<'a> {
399    name: &'a str,
400    receiver: ZngurMethodReceiver,
401    generics: Vec<ParsedRustType<'a>>,
402    inputs: Vec<ParsedRustType<'a>>,
403    output: ParsedRustType<'a>,
404}
405
406impl ParsedMethod<'_> {
407    fn to_zngur(self, scope: &Scope<'_>) -> ZngurMethod {
408        ZngurMethod {
409            name: self.name.to_owned(),
410            generics: self
411                .generics
412                .into_iter()
413                .map(|x| x.to_zngur(scope))
414                .collect(),
415            receiver: self.receiver,
416            inputs: self.inputs.into_iter().map(|x| x.to_zngur(scope)).collect(),
417            output: self.output.to_zngur(scope),
418            is_safe: true,
419        }
420    }
421}
422
423fn checked_merge<T, U>(src: T, dst: &mut U, span: Span, ctx: &mut ParseContext)
424where
425    T: Merge<U>,
426{
427    match src.merge(dst) {
428        Ok(()) => {}
429        Err(e) => match e {
430            MergeFailure::Conflict(s) => {
431                ctx.add_error_str(&s, span);
432            }
433        },
434    }
435}
436
437impl ProcessedItem<'_> {
438    fn add_to_zngur_spec(
439        self,
440        r: &mut ZngurSpecBuilder,
441        scope: &Scope<'_>,
442        ctx: &mut ParseContext,
443    ) {
444        match self {
445            ProcessedItem::Mod {
446                path,
447                items,
448                aliases,
449            } => {
450                let sub_scope = scope.sub_scope(&aliases, path);
451                for item in items {
452                    item.add_to_zngur_spec(r, &sub_scope, ctx);
453                }
454            }
455            ProcessedItem::Import(path) => {
456                if path.path.is_absolute() {
457                    ctx.add_error_str("Absolute paths imports are not supported.", path.span)
458                }
459                match path.path.components().next() {
460                    Some(Component::CurDir) | Some(Component::ParentDir) => {
461                        r.imports.push(Import(path.path));
462                    }
463                    _ => ctx.add_error_str(
464                        "Module import is not supported. Use a relative path instead.",
465                        path.span,
466                    ),
467                }
468            }
469            ProcessedItem::ModuleImport { path, span: _ } => {
470                r.spec
471                    .imported_modules
472                    .push(ModuleImport { path: path.clone() });
473            }
474            ProcessedItem::Type {
475                ty,
476                items,
477                type_vars,
478            } => {
479                if ty.inner == ParsedRustType::Tuple(vec![]) {
480                    // We add unit type implicitly.
481                    ctx.add_error_str(
482                        "Unit type is declared implicitly. Remove this entirely.",
483                        ty.span,
484                    );
485                }
486
487                let (is_template, scope) = match type_vars {
488                    Some(type_vars) => (true, &scope.with_type_vars(type_vars)),
489                    None => (false, scope),
490                };
491
492                let mut methods = vec![];
493                let mut constructor = None;
494                let mut variants = vec![];
495                let mut fields = vec![];
496                let mut wellknown_traits = vec![];
497                let mut layout = None;
498                let mut layout_span = None;
499                let mut exhaustive = true;
500                let mut cpp_value = None;
501                let mut cpp_ref = None;
502                let mut cpp_stack_owned = None;
503                let mut to_process = items;
504                to_process.reverse(); // create a stack of items to process
505                let check_size_align = |props: Vec<(Spanned<&str>, usize)>| {
506                    let mut size = None;
507                    let mut align = None;
508                    let mut errors = vec![];
509                    for (key, value) in props {
510                        match key.inner {
511                            "size" => size = Some(value),
512                            "align" => align = Some(value),
513                            _ => errors.push(("Unknown property", key.span)),
514                        }
515                    }
516                    if size.is_none() {
517                        errors.push(("Size is not declared for this type", ty.span));
518                    }
519                    if align.is_none() {
520                        errors.push(("Align is not declared for this type", ty.span));
521                    }
522                    if errors.is_empty() {
523                        Ok((size.unwrap(), align.unwrap()))
524                    } else {
525                        Err(errors)
526                    }
527                };
528                while let Some(item) = to_process.pop() {
529                    let item_span = item.span;
530                    let item = item.inner;
531                    match item {
532                        ParsedTypeItem::Layout(span, p) => {
533                            layout = Some(match p {
534                                ParsedLayoutPolicy::StackAllocated(p) => {
535                                    match check_size_align(p) {
536                                        Ok((size, align)) => {
537                                            LayoutPolicy::StackAllocated { size, align }
538                                        }
539                                        Err(errs) => {
540                                            for (msg, span) in errs {
541                                                ctx.add_error_str(msg, span);
542                                            }
543                                            continue;
544                                        }
545                                    }
546                                }
547                                ParsedLayoutPolicy::Conservative(p) => match check_size_align(p) {
548                                    Ok((size, align)) => LayoutPolicy::Conservative { size, align },
549                                    Err(errs) => {
550                                        for (msg, span) in errs {
551                                            ctx.add_error_str(msg, span);
552                                        }
553                                        continue;
554                                    }
555                                },
556                                ParsedLayoutPolicy::HeapAllocated => LayoutPolicy::HeapAllocated,
557                                ParsedLayoutPolicy::OnlyByRef => LayoutPolicy::OnlyByRef,
558                            });
559                            match layout_span {
560                                Some(_) => {
561                                    ctx.add_error_str("Duplicate layout policy found", span);
562                                }
563                                None => layout_span = Some(span),
564                            }
565                        }
566                        ParsedTypeItem::Traits(tr) => {
567                            wellknown_traits.extend(tr);
568                        }
569                        ParsedTypeItem::NonExhaustive => {
570                            if !exhaustive {
571                                ctx.add_error_str(
572                                    "Duplicate non_exhaustive annotation found",
573                                    item_span,
574                                );
575                            }
576                            exhaustive = false;
577                        }
578                        ParsedTypeItem::Constructor { args } => {
579                            if constructor.is_some() {
580                                ctx.add_error_str("Duplicate constructor found", item_span);
581                            }
582                            constructor = Some(ZngurConstructor {
583                                inputs: match args {
584                                    ParsedConstructorArgs::Unit => vec![],
585                                    ParsedConstructorArgs::Tuple(t) => t
586                                        .into_iter()
587                                        .enumerate()
588                                        .map(|(i, t)| (i.to_string(), t.to_zngur(scope)))
589                                        .collect(),
590                                    ParsedConstructorArgs::Named(t) => t
591                                        .into_iter()
592                                        .map(|(i, t)| (i.to_owned(), t.to_zngur(scope)))
593                                        .collect(),
594                                },
595                            });
596                        }
597                        ParsedTypeItem::Variant { name, items } => {
598                            let mut exhaustive = true;
599                            let mut fields = vec![];
600                            for item in items {
601                                match item.inner {
602                                    ParsedTypeItem::NonExhaustive => {
603                                        if !exhaustive {
604                                            ctx.add_error_str(
605                                                "Duplicate non_exhaustive annotation found",
606                                                item.span,
607                                            );
608                                        }
609                                        exhaustive = false;
610                                    }
611                                    ParsedTypeItem::Field { name, ty, offset } => {
612                                        if offset.is_some() {
613                                            ctx.add_error_str(
614                                                "static offsets on enum fields are not supported",
615                                                item.span,
616                                            );
617                                        }
618                                        fields.push(ZngurField {
619                                            name: name.to_owned(),
620                                            ty: ty.to_zngur(scope),
621                                            offset,
622                                        });
623                                    }
624                                    _ => panic!("bug: invalid variant item found: {item:?}"),
625                                }
626                            }
627                            variants.push(ZngurVariant {
628                                name: name.to_owned(),
629                                fields,
630                                exhaustive,
631                            });
632                        }
633                        ParsedTypeItem::Field { name, ty, offset } => {
634                            fields.push(ZngurField {
635                                name: name.to_owned(),
636                                ty: ty.to_zngur(scope),
637                                offset,
638                            });
639                        }
640                        ParsedTypeItem::Method {
641                            data,
642                            use_path,
643                            deref,
644                            cpp_name,
645                        } => {
646                            let deref = deref.and_then(|x| {
647                                let deref_type = x.to_zngur(scope);
648                                let receiver_mutability = match data.receiver {
649                                    ZngurMethodReceiver::Ref(mutability) => mutability,
650                                    ZngurMethodReceiver::Static | ZngurMethodReceiver::Move => {
651                                        ctx.add_error_str(
652                                            "Deref needs reference receiver",
653                                            item_span,
654                                        );
655                                        return None;
656                                    }
657                                };
658                                Some((deref_type, receiver_mutability))
659                            });
660                            methods.push(ZngurMethodDetails {
661                                data: data.to_zngur(scope),
662                                use_path: use_path.map(|x| scope.resolve_path(x)),
663                                deref,
664                                cpp_name: cpp_name.map(|s| s.to_owned()),
665                            });
666                        }
667                        ParsedTypeItem::CppValue { field, cpp_type } => {
668                            cpp_value = Some(CppValue(field.to_owned(), cpp_type.to_owned()));
669                        }
670                        ParsedTypeItem::CppRef { cpp_type } => {
671                            match layout_span {
672                                Some(span) => {
673                                    ctx.add_error_str("Duplicate layout policy found", span);
674                                    continue;
675                                }
676                                None => {
677                                    layout = Some(LayoutPolicy::ZERO_SIZED_TYPE);
678                                    layout_span = Some(item_span);
679                                }
680                            }
681                            cpp_ref = Some(CppRef(cpp_type.to_owned()));
682                        }
683                        ParsedTypeItem::CppStackOwned { cpp_type, props } => {
684                            let (size, align) = match check_size_align(props) {
685                                Ok(x) => x,
686                                Err(errs) => {
687                                    for (msg, span) in errs {
688                                        ctx.add_error_str(msg, span);
689                                    }
690                                    continue;
691                                }
692                            };
693                            cpp_stack_owned = Some(CppStackOwned {
694                                cpp_type: cpp_type.to_owned(),
695                                size,
696                                align,
697                            });
698                            layout = Some(LayoutPolicy::StackAllocated { size, align });
699                        }
700                        ParsedTypeItem::MatchOnCfg(match_) => {
701                            let result = match_.eval(ctx);
702                            if let Some(result) = result {
703                                to_process.extend(result);
704                            }
705                        }
706                    }
707                }
708                let is_unsized = wellknown_traits
709                    .iter()
710                    .find(|x| x.inner == ZngurWellknownTrait::Unsized)
711                    .cloned();
712                let wt = wellknown_traits
713                    .into_iter()
714                    .map(|x| x.inner)
715                    .collect::<Vec<_>>();
716                if let Some(is_unsized) = is_unsized {
717                    if let Some(span) = layout_span {
718                        ctx.add_report(
719                            Report::build(
720                                ReportKind::Error,
721                                ctx.filename().to_string(),
722                                span.start,
723                            )
724                            .with_message("Duplicate layout policy found for unsized type.")
725                            .with_label(
726                                Label::new((ctx.filename().to_string(), span.start..span.end))
727                                    .with_message(
728                                        "Unsized types have implicit layout policy, remove this.",
729                                    )
730                                    .with_color(Color::Red),
731                            )
732                            .with_label(
733                                Label::new((
734                                    ctx.filename().to_string(),
735                                    is_unsized.span.start..is_unsized.span.end,
736                                ))
737                                .with_message("Type declared as unsized here.")
738                                .with_color(Color::Blue),
739                            )
740                            .finish(),
741                        )
742                    }
743                    layout = Some(LayoutPolicy::OnlyByRef);
744                }
745                let zngur_type = ZngurType {
746                    ty: ty.inner.to_zngur(scope),
747                    layout,
748                    methods,
749                    wellknown_traits: wt,
750                    exhaustive,
751                    constructor,
752                    variants,
753                    fields,
754                    cpp_value,
755                    cpp_ref,
756                    cpp_stack_owned,
757                };
758                if is_template {
759                    r.templates.push(TemplateDef {
760                        ty: zngur_type,
761                        filename: ctx.filename().to_owned(),
762                        span: ty.span,
763                    });
764                } else {
765                    r.ty_to_locations
766                        .entry(zngur_type.ty.clone())
767                        .or_default()
768                        .push((ctx.filename().to_owned(), ty.span.start..ty.span.end));
769                    checked_merge(zngur_type, &mut r.spec, ty.span, ctx);
770                }
771            }
772            ProcessedItem::Trait { tr, methods } => {
773                checked_merge(
774                    ZngurTrait {
775                        tr: tr.inner.to_zngur(scope),
776                        methods: methods.into_iter().map(|m| m.to_zngur(scope)).collect(),
777                    },
778                    &mut r.spec,
779                    tr.span,
780                    ctx,
781                );
782            }
783            ProcessedItem::Fn(f) => {
784                let method = f.inner.to_zngur(scope);
785                checked_merge(
786                    ZngurFn {
787                        path: RustPathAndGenerics {
788                            path: scope.simple_relative_path(&method.name),
789                            generics: method.generics,
790                            named_generics: vec![],
791                        },
792                        inputs: method.inputs,
793                        output: method.output,
794                    },
795                    &mut r.spec,
796                    f.span,
797                    ctx,
798                );
799            }
800            ProcessedItem::ExternCpp(items) => {
801                for item in items {
802                    match item {
803                        ParsedExternCppItem::Function { is_safe, method } => {
804                            let span = method.span;
805                            let method = method.inner.to_zngur(scope);
806                            checked_merge(
807                                ZngurExternCppFn {
808                                    name: method.name.to_string(),
809                                    inputs: method.inputs,
810                                    output: method.output,
811                                    is_safe,
812                                },
813                                &mut r.spec,
814                                span,
815                                ctx,
816                            );
817                        }
818                        ParsedExternCppItem::Impl { tr, ty, methods } => {
819                            checked_merge(
820                                ZngurExternCppImpl {
821                                    tr: tr.map(|x| x.to_zngur(scope)),
822                                    ty: ty.inner.to_zngur(scope),
823                                    methods: methods
824                                        .into_iter()
825                                        .map(|(is_safe, x)| {
826                                            let mut m = x.to_zngur(scope);
827                                            m.is_safe = is_safe;
828                                            m
829                                        })
830                                        .collect(),
831                                },
832                                &mut r.spec,
833                                ty.span,
834                                ctx,
835                            );
836                        }
837                    }
838                }
839            }
840            ProcessedItem::CppAdditionalInclude(s) => {
841                match AdditionalIncludes(s.to_owned()).merge(&mut r.spec) {
842                    Ok(()) => {}
843                    Err(_) => {
844                        unreachable!() // For now, additional includes can't have conflicts.
845                    }
846                }
847            }
848            ProcessedItem::ConvertPanicToException(span) => {
849                if ctx.depth > 0 {
850                    ctx.add_error_str(
851                        "Using `#convert_panic_to_exception` in imported zngur files is not supported. This directive can only be used in the main zngur file.",
852                        span,
853                    );
854                    return;
855                }
856                match ConvertPanicToException(true).merge(&mut r.spec) {
857                    Ok(()) => {}
858                    Err(_) => {
859                        unreachable!() // For now, CPtE also can't have conflicts.
860                    }
861                }
862            }
863        }
864    }
865}
866
867#[derive(Debug, Clone, PartialEq, Eq)]
868enum ParsedRustType<'a> {
869    Primitive(PrimitiveRustType),
870    Ref(Mutability, Box<ParsedRustType<'a>>),
871    Raw(Mutability, Box<ParsedRustType<'a>>),
872    Boxed(Box<ParsedRustType<'a>>),
873    Slice(Box<ParsedRustType<'a>>),
874    Dyn(ParsedRustTrait<'a>, Vec<&'a str>),
875    Impl(ParsedRustTrait<'a>, Vec<&'a str>),
876    Tuple(Vec<ParsedRustType<'a>>),
877    Adt(ParsedRustPathAndGenerics<'a>),
878}
879
880impl ParsedRustType<'_> {
881    fn to_zngur(self, scope: &Scope<'_>) -> RustType {
882        match self {
883            ParsedRustType::Primitive(s) => RustType::Primitive(s),
884            ParsedRustType::Ref(m, s) => RustType::Ref(m, Box::new(s.to_zngur(scope))),
885            ParsedRustType::Raw(m, s) => RustType::Raw(m, Box::new(s.to_zngur(scope))),
886            ParsedRustType::Boxed(s) => RustType::Boxed(Box::new(s.to_zngur(scope))),
887            ParsedRustType::Slice(s) => RustType::Slice(Box::new(s.to_zngur(scope))),
888            ParsedRustType::Dyn(tr, bounds) => RustType::Dyn(
889                tr.to_zngur(scope),
890                bounds.into_iter().map(|x| x.to_owned()).collect(),
891            ),
892            ParsedRustType::Impl(tr, bounds) => RustType::Impl(
893                tr.to_zngur(scope),
894                bounds.into_iter().map(|x| x.to_owned()).collect(),
895            ),
896            ParsedRustType::Tuple(v) => {
897                RustType::Tuple(v.into_iter().map(|s| s.to_zngur(scope)).collect())
898            }
899            ParsedRustType::Adt(s) => match scope.as_type_var(&s) {
900                Some(v) => RustType::TypeVar(v),
901                None => RustType::Adt(s.to_zngur(scope)),
902            },
903        }
904    }
905}
906
907#[derive(Debug, Clone, PartialEq, Eq)]
908enum ParsedRustTrait<'a> {
909    Normal(ParsedRustPathAndGenerics<'a>),
910    Fn {
911        name: &'a str,
912        inputs: Vec<ParsedRustType<'a>>,
913        output: Box<ParsedRustType<'a>>,
914    },
915}
916
917impl ParsedRustTrait<'_> {
918    fn to_zngur(self, scope: &Scope<'_>) -> RustTrait {
919        match self {
920            ParsedRustTrait::Normal(s) => RustTrait::Normal(s.to_zngur(scope)),
921            ParsedRustTrait::Fn {
922                name,
923                inputs,
924                output,
925            } => RustTrait::Fn {
926                name: name.to_owned(),
927                inputs: inputs.into_iter().map(|s| s.to_zngur(scope)).collect(),
928                output: Box::new(output.to_zngur(scope)),
929            },
930        }
931    }
932}
933
934#[derive(Debug, Clone, PartialEq, Eq)]
935struct ParsedRustPathAndGenerics<'a> {
936    path: ParsedPath<'a>,
937    generics: Vec<ParsedRustType<'a>>,
938    named_generics: Vec<(&'a str, ParsedRustType<'a>)>,
939}
940
941impl ParsedRustPathAndGenerics<'_> {
942    fn to_zngur(self, scope: &Scope<'_>) -> RustPathAndGenerics {
943        RustPathAndGenerics {
944            path: scope.resolve_path(self.path),
945            generics: self
946                .generics
947                .into_iter()
948                .map(|x| x.to_zngur(scope))
949                .collect(),
950            named_generics: self
951                .named_generics
952                .into_iter()
953                .map(|(name, x)| (name.to_owned(), x.to_zngur(scope)))
954                .collect(),
955        }
956    }
957}
958
959struct ParseContext<'a, 'b> {
960    path: std::path::PathBuf,
961    text: &'a str,
962    depth: usize,
963    reports: Vec<Report<'b, (String, std::ops::Range<usize>)>>,
964    source_cache: std::collections::HashMap<std::path::PathBuf, String>,
965    /// All .zng files processed during parsing (main file + imports)
966    processed_files: Vec<std::path::PathBuf>,
967    cfg_provider: Box<dyn RustCfgProvider>,
968}
969
970impl<'a, 'b> ParseContext<'a, 'b> {
971    fn new(path: std::path::PathBuf, text: &'a str, cfg: Box<dyn RustCfgProvider>) -> Self {
972        let processed_files = vec![path.clone()];
973        Self {
974            path,
975            text,
976            depth: 0,
977            reports: Vec::new(),
978            source_cache: HashMap::new(),
979            processed_files,
980            cfg_provider: cfg,
981        }
982    }
983
984    fn with_depth(
985        path: std::path::PathBuf,
986        text: &'a str,
987        depth: usize,
988        cfg: Box<dyn RustCfgProvider>,
989    ) -> Self {
990        let processed_files = vec![path.clone()];
991        Self {
992            path,
993            text,
994            depth,
995            reports: Vec::new(),
996            source_cache: HashMap::new(),
997            processed_files,
998            cfg_provider: cfg,
999        }
1000    }
1001
1002    fn filename(&self) -> &str {
1003        self.path.file_name().unwrap().to_str().unwrap()
1004    }
1005
1006    fn add_report(&mut self, report: Report<'b, (String, std::ops::Range<usize>)>) {
1007        self.reports.push(report);
1008    }
1009    fn add_errors<'err_src>(&mut self, errs: impl Iterator<Item = Rich<'err_src, String>>) {
1010        let filename = self.filename().to_string();
1011        self.reports.extend(errs.map(|e| {
1012            Report::build(ReportKind::Error, &filename, e.span().start)
1013                .with_message(e.to_string())
1014                .with_label(
1015                    Label::new((filename.clone(), e.span().into_range()))
1016                        .with_message(e.reason().to_string())
1017                        .with_color(Color::Red),
1018                )
1019                .with_labels(e.contexts().map(|(label, span)| {
1020                    Label::new((filename.clone(), span.into_range()))
1021                        .with_message(format!("while parsing this {}", label))
1022                        .with_color(Color::Yellow)
1023                }))
1024                .finish()
1025        }));
1026    }
1027
1028    fn add_error_str(&mut self, error: &str, span: Span) {
1029        self.add_errors([Rich::custom(span, error)].into_iter());
1030    }
1031
1032    fn consume_from(&mut self, mut other: ParseContext<'_, 'b>) {
1033        self.processed_files.append(&mut other.processed_files);
1034        self.reports.extend(other.reports);
1035        // Always cache the source in case errors come up in post-processing
1036        self.source_cache.insert(other.path, other.text.to_string());
1037        self.source_cache.extend(other.source_cache);
1038    }
1039
1040    fn has_errors(&self) -> bool {
1041        !self.reports.is_empty()
1042    }
1043
1044    #[cfg(test)]
1045    fn emit_ariadne_errors(&self) -> ! {
1046        let mut r = Vec::<u8>::new();
1047        for err in &self.reports {
1048            err.write(
1049                sources(
1050                    [(self.filename().to_string(), self.text)]
1051                        .into_iter()
1052                        .chain(
1053                            self.source_cache
1054                                .iter()
1055                                .map(|(path, text)| {
1056                                    (
1057                                        path.file_name().unwrap().to_str().unwrap().to_string(),
1058                                        text.as_str(),
1059                                    )
1060                                })
1061                                .collect::<Vec<_>>()
1062                                .into_iter(),
1063                        ),
1064                ),
1065                &mut r,
1066            )
1067            .unwrap();
1068        }
1069        std::panic::resume_unwind(Box::new(tests::ErrorText({
1070            let s = String::from_utf8(strip_ansi_escapes::strip(r)).unwrap();
1071            eprintln!("{s}");
1072            s
1073        })));
1074    }
1075
1076    #[cfg(not(test))]
1077    fn emit_ariadne_errors(&self) -> ! {
1078        for err in &self.reports {
1079            err.eprint(sources(
1080                [(self.filename().to_string(), self.text)]
1081                    .into_iter()
1082                    .chain(
1083                        self.source_cache
1084                            .iter()
1085                            .map(|(path, text)| {
1086                                (
1087                                    path.file_name().unwrap().to_str().unwrap().to_string(),
1088                                    text.as_str(),
1089                                )
1090                            })
1091                            .collect::<Vec<_>>()
1092                            .into_iter(),
1093                    ),
1094            ))
1095            .unwrap();
1096        }
1097        exit(101);
1098    }
1099
1100    fn get_config_provider(&self) -> &dyn RustCfgProvider {
1101        self.cfg_provider.as_ref()
1102    }
1103}
1104
1105/// A trait for types which can resolve filesystem-like paths relative to a given directory.
1106pub trait ImportResolver {
1107    fn resolve_import(
1108        &self,
1109        cwd: &std::path::Path,
1110        relpath: &std::path::Path,
1111    ) -> Result<String, String>;
1112}
1113
1114/// A default implementation of ImportResolver which uses conventional filesystem paths and semantics.
1115struct DefaultImportResolver;
1116
1117impl ImportResolver for DefaultImportResolver {
1118    fn resolve_import(
1119        &self,
1120        cwd: &std::path::Path,
1121        relpath: &std::path::Path,
1122    ) -> Result<String, String> {
1123        let path = cwd
1124            .join(relpath)
1125            .canonicalize()
1126            .map_err(|e| e.to_string())?;
1127        std::fs::read_to_string(path).map_err(|e| e.to_string())
1128    }
1129}
1130
1131impl<'a> ParsedZngFile<'a> {
1132    fn parse_into(
1133        zngur: &mut ZngurSpecBuilder,
1134        ctx: &mut ParseContext,
1135        resolver: &impl ImportResolver,
1136    ) {
1137        let (tokens, errs) = lexer().parse(ctx.text).into_output_errors();
1138        let Some(tokens) = tokens else {
1139            ctx.add_errors(errs.into_iter().map(|e| e.map_token(|c| c.to_string())));
1140            ctx.emit_ariadne_errors();
1141        };
1142        let tokens: ParserInput<'_> = tokens.as_slice().map(
1143            (ctx.text.len()..ctx.text.len()).into(),
1144            Box::new(|(t, s)| (t, s)),
1145        );
1146        let (ast, errs) = file_parser()
1147            .map_with(|ast, extra| (ast, extra.span()))
1148            .parse_with_state(tokens, &mut extra::SimpleState(ZngParserState::default()))
1149            .into_output_errors();
1150        let Some(ast) = ast else {
1151            ctx.add_errors(errs.into_iter().map(|e| e.map_token(|c| c.to_string())));
1152            ctx.emit_ariadne_errors();
1153        };
1154
1155        let (aliases, items) = partition_parsed_items(
1156            ast.0
1157                .0
1158                .into_iter()
1159                .map(|item| process_parsed_item(item, ctx)),
1160        );
1161        ProcessedZngFile::new(aliases, items).into_zngur_spec(zngur, ctx);
1162
1163        if let Some(dirname) = ctx.path.to_owned().parent() {
1164            for import in std::mem::take(&mut zngur.imports) {
1165                match resolver.resolve_import(dirname, &import.0) {
1166                    Ok(text) => {
1167                        let mut nested_ctx = ParseContext::with_depth(
1168                            dirname.join(&import.0),
1169                            &text,
1170                            ctx.depth + 1,
1171                            ctx.get_config_provider().clone_box(),
1172                        );
1173                        Self::parse_into(zngur, &mut nested_ctx, resolver);
1174                        ctx.consume_from(nested_ctx);
1175                    }
1176                    Err(_) => {
1177                        // TODO: emit a better error. How should we get a span here?
1178                        // I'd like to avoid putting a ParsedImportPath in ZngurSpec, and
1179                        // also not have to pass a filename to add_to_zngur_spec.
1180                        ctx.add_report(
1181                            Report::build(ReportKind::Error, ctx.filename(), 0)
1182                                .with_message(format!(
1183                                    "Import path not found: {}",
1184                                    import.0.display()
1185                                ))
1186                                .finish(),
1187                        );
1188                    }
1189                }
1190            }
1191        }
1192    }
1193
1194    /// Parse a .zng file and return both the spec and list of all processed files.
1195    pub fn parse(path: std::path::PathBuf, cfg: Box<dyn RustCfgProvider>) -> ParseResult {
1196        let mut zngur = ZngurSpecBuilder::default();
1197        zngur.spec.rust_cfg.extend(cfg.get_cfg_pairs());
1198        zngur.spec.rust_cfg.sort();
1199        let text = std::fs::read_to_string(&path).unwrap();
1200        let mut ctx = ParseContext::new(path.clone(), &text, cfg.clone_box());
1201        Self::parse_into(&mut zngur, &mut ctx, &DefaultImportResolver);
1202        let spec = zngur.to_zngur(&mut ctx);
1203        if ctx.has_errors() {
1204            // add report of cfg values used
1205            ctx.add_report(
1206                Report::build(
1207                    ReportKind::Custom("cfg values", ariadne::Color::Green),
1208                    path.file_name().unwrap_or_default().to_string_lossy(),
1209                    0,
1210                )
1211                .with_message(
1212                    cfg.get_cfg_pairs()
1213                        .into_iter()
1214                        .map(|(key, value)| match value {
1215                            Some(value) => format!("{key}=\"{value}\""),
1216                            None => key,
1217                        })
1218                        .join("\n")
1219                        .to_string(),
1220                )
1221                .finish(),
1222            );
1223            ctx.emit_ariadne_errors();
1224        }
1225        ParseResult {
1226            spec,
1227            processed_files: ctx.processed_files,
1228        }
1229    }
1230
1231    /// Parse a .zng file from a string. Mainly useful for testing.
1232    #[cfg(test)]
1233    pub fn parse_str(text: &str, cfg: impl RustCfgProvider + 'static) -> ParseResult {
1234        Self::parse_str_with_resolver(text, cfg, &DefaultImportResolver)
1235    }
1236
1237    #[cfg(test)]
1238    pub(crate) fn parse_str_with_resolver(
1239        text: &str,
1240        cfg: impl RustCfgProvider + 'static,
1241        resolver: &impl ImportResolver,
1242    ) -> ParseResult {
1243        let mut zngur = ZngurSpecBuilder::default();
1244        let mut ctx = ParseContext::new(std::path::PathBuf::from("test.zng"), text, Box::new(cfg));
1245        Self::parse_into(&mut zngur, &mut ctx, resolver);
1246        let spec = zngur.to_zngur(&mut ctx);
1247        if ctx.has_errors() {
1248            ctx.emit_ariadne_errors();
1249        }
1250        ParseResult {
1251            spec,
1252            processed_files: ctx.processed_files,
1253        }
1254    }
1255}
1256
1257pub(crate) enum ProcessedItemOrAlias<'a> {
1258    Ignore,
1259    Processed(ProcessedItem<'a>),
1260    Alias(ParsedAlias<'a>),
1261    ChildItems(Vec<ProcessedItemOrAlias<'a>>),
1262}
1263
1264fn process_parsed_item<'a>(
1265    item: ParsedItem<'a>,
1266    ctx: &mut ParseContext,
1267) -> ProcessedItemOrAlias<'a> {
1268    use ProcessedItemOrAlias as Ret;
1269    match item {
1270        ParsedItem::Alias(alias) => Ret::Alias(alias),
1271        ParsedItem::ConvertPanicToException(span) => {
1272            Ret::Processed(ProcessedItem::ConvertPanicToException(span))
1273        }
1274        ParsedItem::UnstableFeature(_) => {
1275            // ignore
1276            Ret::Ignore
1277        }
1278        ParsedItem::CppAdditionalInclude(inc) => {
1279            Ret::Processed(ProcessedItem::CppAdditionalInclude(inc))
1280        }
1281        ParsedItem::Mod { path, items } => {
1282            let (aliases, items) = partition_parsed_items(
1283                items.into_iter().map(|item| process_parsed_item(item, ctx)),
1284            );
1285            Ret::Processed(ProcessedItem::Mod {
1286                path,
1287                items,
1288                aliases,
1289            })
1290        }
1291        ParsedItem::Type {
1292            ty,
1293            items,
1294            type_vars,
1295        } => Ret::Processed(ProcessedItem::Type {
1296            ty,
1297            items,
1298            type_vars,
1299        }),
1300        ParsedItem::Trait { tr, methods } => Ret::Processed(ProcessedItem::Trait { tr, methods }),
1301        ParsedItem::Fn(method) => Ret::Processed(ProcessedItem::Fn(method)),
1302        ParsedItem::ExternCpp(items) => Ret::Processed(ProcessedItem::ExternCpp(items)),
1303        ParsedItem::Import(path) => Ret::Processed(ProcessedItem::Import(path)),
1304        ParsedItem::ModuleImport { path, span } => {
1305            Ret::Processed(ProcessedItem::ModuleImport { path, span })
1306        }
1307        ParsedItem::MatchOnCfg(match_) => Ret::ChildItems(
1308            match_
1309                .eval(ctx)
1310                .unwrap_or_default() // unwrap or empty
1311                .into_iter()
1312                .map(|item| item.inner)
1313                .collect(),
1314        ),
1315    }
1316}
1317
1318fn partition_parsed_items<'a>(
1319    items: impl IntoIterator<Item = ProcessedItemOrAlias<'a>>,
1320) -> (Vec<ParsedAlias<'a>>, Vec<ProcessedItem<'a>>) {
1321    let mut aliases = Vec::new();
1322    let mut processed = Vec::new();
1323    for item in items.into_iter() {
1324        match item {
1325            ProcessedItemOrAlias::Ignore => continue,
1326            ProcessedItemOrAlias::Processed(p) => processed.push(p),
1327            ProcessedItemOrAlias::Alias(a) => aliases.push(a),
1328            ProcessedItemOrAlias::ChildItems(children) => {
1329                let (child_aliases, child_items) = partition_parsed_items(children);
1330                aliases.extend(child_aliases);
1331                processed.extend(child_items);
1332            }
1333        }
1334    }
1335    (aliases, processed)
1336}
1337
1338impl<'a> ProcessedZngFile<'a> {
1339    fn new(aliases: Vec<ParsedAlias<'a>>, items: Vec<ProcessedItem<'a>>) -> Self {
1340        ProcessedZngFile { aliases, items }
1341    }
1342
1343    fn into_zngur_spec(self, zngur: &mut ZngurSpecBuilder, ctx: &mut ParseContext) {
1344        let root_scope = Scope::new_root(self.aliases);
1345
1346        for item in self.items {
1347            item.add_to_zngur_spec(zngur, &root_scope, ctx);
1348        }
1349    }
1350}
1351
1352struct TemplateDef {
1353    ty: ZngurType,
1354    filename: String,
1355    span: Span,
1356}
1357
1358#[derive(Default)]
1359struct ZngurSpecBuilder {
1360    spec: ZngurSpec,
1361    templates: Vec<TemplateDef>,
1362    ty_to_locations: HashMap<RustType, Vec<(String, std::ops::Range<usize>)>>,
1363    imports: Vec<Import>,
1364}
1365
1366impl ZngurSpecBuilder {
1367    fn to_zngur(self, ctx: &mut ParseContext) -> ZngurSpec {
1368        let ZngurSpecBuilder {
1369            mut spec,
1370            templates,
1371            imports: _,
1372            mut ty_to_locations,
1373        } = self;
1374        for ty in &mut spec.types {
1375            let mut template_locations = Vec::new();
1376            for template in &templates {
1377                if let Some(template_match) = try_match_template(&ty.ty, &template.ty) {
1378                    let location = (
1379                        template.filename.clone(),
1380                        template.span.start..template.span.end,
1381                    );
1382                    if let Err(e) = template_match.merge(ty) {
1383                        let MergeFailure::Conflict(e) = e;
1384                        ctx.add_report(
1385                            Report::build(ReportKind::Error, &template.filename, 0)
1386                                .with_message(format!(
1387                                    "Failed to apply template {} to type {}: {}",
1388                                    template.ty.ty, ty.ty, e
1389                                ))
1390                                .with_label(
1391                                    Label::new(location)
1392                                        .with_message("Template defined here")
1393                                        .with_color(Color::Blue),
1394                                )
1395                                .finish(),
1396                        );
1397                    } else {
1398                        template_locations.push(location);
1399                    }
1400                }
1401            }
1402            if !ty.wellknown_traits.iter().any(|wkt| {
1403                matches!(
1404                    wkt,
1405                    ZngurWellknownTrait::Copy | ZngurWellknownTrait::Unsized
1406                )
1407            }) {
1408                ty.wellknown_traits.push(ZngurWellknownTrait::Drop);
1409            }
1410            if ty.layout.is_none() {
1411                let mut report = Report::build(ReportKind::Error, "", 0).with_message(format!(
1412                    "No layout policy found for type {}. \
1413    Use one of `#layout(size = X, align = Y)`, `#heap_allocated` or `#only_by_ref`.",
1414                    ty.ty
1415                ));
1416                for location in ty_to_locations.remove(&ty.ty).unwrap_or_default() {
1417                    report = report.with_label(
1418                        Label::new(location)
1419                            .with_message("Type defined here")
1420                            .with_color(Color::Blue),
1421                    );
1422                }
1423                for location in template_locations {
1424                    report = report.with_label(
1425                        Label::new(location)
1426                            .with_message("Matching template defined here")
1427                            .with_color(Color::Blue),
1428                    );
1429                }
1430                ctx.add_report(report.finish());
1431            }
1432        }
1433        spec
1434    }
1435}
1436
1437#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1438enum Token<'a> {
1439    Arrow,
1440    ArrowArm,
1441    AngleOpen,
1442    AngleClose,
1443    BracketOpen,
1444    BracketClose,
1445    Colon,
1446    ColonColon,
1447    ParenOpen,
1448    ParenClose,
1449    BraceOpen,
1450    BraceClose,
1451    And,
1452    Star,
1453    Sharp,
1454    Plus,
1455    Eq,
1456    Question,
1457    Comma,
1458    Semicolon,
1459    Pipe,
1460    Underscore,
1461    Dot,
1462    Bang,
1463    KwAs,
1464    KwAsync,
1465    KwDyn,
1466    KwUse,
1467    KwFor,
1468    KwMod,
1469    KwCrate,
1470    KwType,
1471    KwTrait,
1472    KwFn,
1473    KwMut,
1474    KwConst,
1475    KwExtern,
1476    KwImpl,
1477    KwImport,
1478    KwMerge,
1479    KwIf,
1480    KwElse,
1481    KwMatch,
1482    KwSafe,
1483    KwUnsafe,
1484    Ident(&'a str),
1485    Str(&'a str),
1486    RawStr(usize, &'a str),
1487    Number(usize),
1488}
1489
1490impl<'a> Token<'a> {
1491    fn ident_or_kw(ident: &'a str) -> Self {
1492        match ident {
1493            "as" => Token::KwAs,
1494            "async" => Token::KwAsync,
1495            "dyn" => Token::KwDyn,
1496            "mod" => Token::KwMod,
1497            "type" => Token::KwType,
1498            "trait" => Token::KwTrait,
1499            "crate" => Token::KwCrate,
1500            "fn" => Token::KwFn,
1501            "mut" => Token::KwMut,
1502            "const" => Token::KwConst,
1503            "use" => Token::KwUse,
1504            "for" => Token::KwFor,
1505            "extern" => Token::KwExtern,
1506            "impl" => Token::KwImpl,
1507            "import" => Token::KwImport,
1508            "merge" => Token::KwMerge,
1509            "if" => Token::KwIf,
1510            "else" => Token::KwElse,
1511            "match" => Token::KwMatch,
1512            "safe" => Token::KwSafe,
1513            "unsafe" => Token::KwUnsafe,
1514            x => Token::Ident(x),
1515        }
1516    }
1517}
1518
1519impl Display for Token<'_> {
1520    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1521        match self {
1522            Token::Arrow => write!(f, "->"),
1523            Token::ArrowArm => write!(f, "=>"),
1524            Token::AngleOpen => write!(f, "<"),
1525            Token::AngleClose => write!(f, ">"),
1526            Token::BracketOpen => write!(f, "["),
1527            Token::BracketClose => write!(f, "]"),
1528            Token::ParenOpen => write!(f, "("),
1529            Token::ParenClose => write!(f, ")"),
1530            Token::BraceOpen => write!(f, "{{"),
1531            Token::BraceClose => write!(f, "}}"),
1532            Token::Colon => write!(f, ":"),
1533            Token::ColonColon => write!(f, "::"),
1534            Token::And => write!(f, "&"),
1535            Token::Star => write!(f, "*"),
1536            Token::Sharp => write!(f, "#"),
1537            Token::Plus => write!(f, "+"),
1538            Token::Eq => write!(f, "="),
1539            Token::Question => write!(f, "?"),
1540            Token::Comma => write!(f, ","),
1541            Token::Semicolon => write!(f, ";"),
1542            Token::Pipe => write!(f, "|"),
1543            Token::Underscore => write!(f, "_"),
1544            Token::Dot => write!(f, "."),
1545            Token::Bang => write!(f, "!"),
1546            Token::KwAs => write!(f, "as"),
1547            Token::KwAsync => write!(f, "async"),
1548            Token::KwDyn => write!(f, "dyn"),
1549            Token::KwUse => write!(f, "use"),
1550            Token::KwFor => write!(f, "for"),
1551            Token::KwMod => write!(f, "mod"),
1552            Token::KwCrate => write!(f, "crate"),
1553            Token::KwType => write!(f, "type"),
1554            Token::KwTrait => write!(f, "trait"),
1555            Token::KwFn => write!(f, "fn"),
1556            Token::KwMut => write!(f, "mut"),
1557            Token::KwConst => write!(f, "const"),
1558            Token::KwExtern => write!(f, "extern"),
1559            Token::KwImpl => write!(f, "impl"),
1560            Token::KwImport => write!(f, "import"),
1561            Token::KwMerge => write!(f, "merge"),
1562            Token::KwIf => write!(f, "if"),
1563            Token::KwElse => write!(f, "else"),
1564            Token::KwMatch => write!(f, "match"),
1565            Token::KwSafe => write!(f, "safe"),
1566            Token::KwUnsafe => write!(f, "unsafe"),
1567            Token::Ident(i) => write!(f, "{i}"),
1568            Token::Number(n) => write!(f, "{n}"),
1569            Token::Str(s) => write!(f, r#""{s}""#),
1570            Token::RawStr(hashes, s) => {
1571                let h = "#".repeat(*hashes);
1572                write!(f, r#"r{h}"{s}"{h}"#)
1573            }
1574        }
1575    }
1576}
1577
1578fn lexer<'src>()
1579-> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, extra::Err<Rich<'src, char, Span>>> {
1580    let plain_string = just('"')
1581        .ignore_then(none_of('"').repeated().to_slice().map(Token::Str))
1582        .then_ignore(just('"'));
1583
1584    let raw_string_start = just('r')
1585        .ignore_then(just('#').repeated().count())
1586        .then_ignore(just('"'));
1587    let raw_string_end =
1588        just('"').then(just('#').repeated().configure(|cfg, ctx| cfg.exactly(*ctx)));
1589    let raw_string = raw_string_start
1590        .then_with_ctx(
1591            any()
1592                .and_is(raw_string_end.not())
1593                .repeated()
1594                .to_slice()
1595                .then_ignore(raw_string_end),
1596        )
1597        .map(|(h, s)| Token::RawStr(h, s));
1598
1599    let token = choice((
1600        choice([
1601            just("->").to(Token::Arrow),
1602            just("=>").to(Token::ArrowArm),
1603            just("<").to(Token::AngleOpen),
1604            just(">").to(Token::AngleClose),
1605            just("[").to(Token::BracketOpen),
1606            just("]").to(Token::BracketClose),
1607            just("(").to(Token::ParenOpen),
1608            just(")").to(Token::ParenClose),
1609            just("{").to(Token::BraceOpen),
1610            just("}").to(Token::BraceClose),
1611            just("::").to(Token::ColonColon),
1612            just(":").to(Token::Colon),
1613            just("&").to(Token::And),
1614            just("*").to(Token::Star),
1615            just("#").to(Token::Sharp),
1616            just("+").to(Token::Plus),
1617            just("=").to(Token::Eq),
1618            just("?").to(Token::Question),
1619            just(",").to(Token::Comma),
1620            just(";").to(Token::Semicolon),
1621            just("|").to(Token::Pipe),
1622            just("_").to(Token::Underscore),
1623            just(".").to(Token::Dot),
1624            just("!").to(Token::Bang),
1625        ]),
1626        raw_string,
1627        plain_string,
1628        text::ident().map(Token::ident_or_kw),
1629        text::int(10).map(|x: &str| Token::Number(x.parse().unwrap())),
1630    ));
1631
1632    let comment = just("//")
1633        .then(any().and_is(just('\n').not()).repeated())
1634        .padded();
1635
1636    token
1637        .map_with(|tok, extra| (tok, extra.span()))
1638        .padded_by(comment.repeated())
1639        .padded()
1640        .repeated()
1641        .collect()
1642        .boxed()
1643}
1644
1645fn alias<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
1646    just(Token::KwUse)
1647        .ignore_then(path())
1648        .then_ignore(just(Token::KwAs))
1649        .then(select! {
1650            Token::Ident(c) => c,
1651        })
1652        .then_ignore(just(Token::Semicolon))
1653        .map_with(|(path, name), extra| {
1654            ParsedItem::Alias(ParsedAlias {
1655                name,
1656                path,
1657                span: extra.span(),
1658            })
1659        })
1660        .boxed()
1661}
1662
1663fn file_parser<'a>()
1664-> impl Parser<'a, ParserInput<'a>, ParsedZngFile<'a>, ZngParserExtra<'a>> + Clone {
1665    item()
1666        .repeated()
1667        .collect::<Vec<_>>()
1668        .map(ParsedZngFile)
1669        .boxed()
1670}
1671
1672fn rust_type<'a>() -> Boxed<'a, 'a, ParserInput<'a>, ParsedRustType<'a>, ZngParserExtra<'a>> {
1673    let as_scalar = |s: &str, head: char| -> Option<u32> {
1674        let s = s.strip_prefix(head)?;
1675        s.parse().ok()
1676    };
1677
1678    let scalar = select! {
1679        Token::Ident("bool") => PrimitiveRustType::Bool,
1680        Token::Ident("str") => PrimitiveRustType::Str,
1681        Token::Ident("char") => PrimitiveRustType::Char,
1682        Token::Ident("usize") => PrimitiveRustType::Usize,
1683        Token::Ident(c) if as_scalar(c, 'u').is_some() => PrimitiveRustType::Uint(as_scalar(c, 'u').unwrap()),
1684        Token::Ident(c) if as_scalar(c, 'i').is_some() => PrimitiveRustType::Int(as_scalar(c, 'i').unwrap()),
1685        Token::Ident(c) if as_scalar(c, 'f').is_some() => PrimitiveRustType::Float(as_scalar(c, 'f').unwrap()),
1686    }.map(ParsedRustType::Primitive);
1687
1688    recursive(|parser| {
1689        let parser = parser.boxed();
1690        let pg = rust_path_and_generics(parser.clone());
1691        let adt = pg.clone().map(ParsedRustType::Adt);
1692
1693        let dyn_trait = just(Token::KwDyn)
1694            .or(just(Token::KwImpl))
1695            .then(rust_trait(parser.clone()))
1696            .then(
1697                just(Token::Plus)
1698                    .ignore_then(select! {
1699                        Token::Ident(c) => c,
1700                    })
1701                    .repeated()
1702                    .collect::<Vec<_>>()
1703                    .boxed(),
1704            )
1705            .map(|((token, first), rest)| match token {
1706                Token::KwDyn => ParsedRustType::Dyn(first, rest),
1707                Token::KwImpl => ParsedRustType::Impl(first, rest),
1708                _ => unreachable!(),
1709            });
1710        let boxed = just(Token::Ident("Box"))
1711            .then(rust_generics(parser.clone()))
1712            .map(|(_, x)| {
1713                assert_eq!(x.len(), 1);
1714                ParsedRustType::Boxed(Box::new(x.into_iter().next().unwrap().right().unwrap()))
1715            });
1716        let unit = just(Token::ParenOpen)
1717            .then(just(Token::ParenClose))
1718            .map(|_| ParsedRustType::Tuple(vec![]));
1719        let tuple = parser
1720            .clone()
1721            .separated_by(just(Token::Comma))
1722            .allow_trailing()
1723            .collect::<Vec<_>>()
1724            .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1725            .map(|xs| ParsedRustType::Tuple(xs));
1726        let slice = parser
1727            .clone()
1728            .map(|x| ParsedRustType::Slice(Box::new(x)))
1729            .delimited_by(just(Token::BracketOpen), just(Token::BracketClose));
1730        let reference = just(Token::And)
1731            .ignore_then(
1732                just(Token::KwMut)
1733                    .to(Mutability::Mut)
1734                    .or(empty().to(Mutability::Not)),
1735            )
1736            .then(parser.clone())
1737            .map(|(m, x)| ParsedRustType::Ref(m, Box::new(x)));
1738        let raw_ptr = just(Token::Star)
1739            .ignore_then(
1740                just(Token::KwMut)
1741                    .to(Mutability::Mut)
1742                    .or(just(Token::KwConst).to(Mutability::Not)),
1743            )
1744            .then(parser)
1745            .map(|(m, x)| ParsedRustType::Raw(m, Box::new(x)));
1746        choice((
1747            scalar.boxed(),
1748            boxed.boxed(),
1749            unit.boxed(),
1750            tuple.boxed(),
1751            slice.boxed(),
1752            adt.boxed(),
1753            reference.boxed(),
1754            raw_ptr.boxed(),
1755            dyn_trait.boxed(),
1756        ))
1757    })
1758    .boxed()
1759}
1760
1761fn rust_generics<'a>(
1762    rust_type: Boxed<'a, 'a, ParserInput<'a>, ParsedRustType<'a>, ZngParserExtra<'a>>,
1763) -> impl Parser<
1764    'a,
1765    ParserInput<'a>,
1766    Vec<Either<(&'a str, ParsedRustType<'a>), ParsedRustType<'a>>>,
1767    ZngParserExtra<'a>,
1768> + Clone {
1769    let named_generic = select! {
1770        Token::Ident(c) => c,
1771    }
1772    .then_ignore(just(Token::Eq))
1773    .then(rust_type.clone())
1774    .map(Either::Left);
1775    just(Token::ColonColon)
1776        .repeated()
1777        .at_most(1)
1778        .ignore_then(
1779            named_generic
1780                .or(rust_type.clone().map(Either::Right))
1781                .separated_by(just(Token::Comma))
1782                .allow_trailing()
1783                .collect::<Vec<_>>()
1784                .delimited_by(just(Token::AngleOpen), just(Token::AngleClose))
1785                .boxed(),
1786        )
1787        .boxed()
1788}
1789
1790fn rust_path_and_generics<'a>(
1791    rust_type: Boxed<'a, 'a, ParserInput<'a>, ParsedRustType<'a>, ZngParserExtra<'a>>,
1792) -> impl Parser<'a, ParserInput<'a>, ParsedRustPathAndGenerics<'a>, ZngParserExtra<'a>> + Clone {
1793    let generics = rust_generics(rust_type.clone());
1794    path()
1795        .then(generics.clone().repeated().at_most(1).collect::<Vec<_>>())
1796        .map(|x| {
1797            let generics = x.1.into_iter().next().unwrap_or_default();
1798            let (named_generics, generics) = generics.into_iter().partition_map(|x| x);
1799            ParsedRustPathAndGenerics {
1800                path: x.0,
1801                generics,
1802                named_generics,
1803            }
1804        })
1805        .boxed()
1806}
1807
1808fn fn_args<'a>(
1809    rust_type: Boxed<'a, 'a, ParserInput<'a>, ParsedRustType<'a>, ZngParserExtra<'a>>,
1810) -> impl Parser<'a, ParserInput<'a>, (Vec<ParsedRustType<'a>>, ParsedRustType<'a>), ZngParserExtra<'a>>
1811+ Clone {
1812    rust_type
1813        .clone()
1814        .separated_by(just(Token::Comma))
1815        .allow_trailing()
1816        .collect::<Vec<_>>()
1817        .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1818        .then(
1819            just(Token::Arrow)
1820                .ignore_then(rust_type)
1821                .or(empty().to(ParsedRustType::Tuple(vec![]))),
1822        )
1823        .boxed()
1824}
1825
1826fn spanned<'a, T>(
1827    parser: impl Parser<'a, ParserInput<'a>, T, ZngParserExtra<'a>> + Clone,
1828) -> impl Parser<'a, ParserInput<'a>, Spanned<T>, ZngParserExtra<'a>> + Clone {
1829    parser.map_with(|inner, extra| Spanned {
1830        inner,
1831        span: extra.span(),
1832    })
1833}
1834
1835fn rust_trait<'a>(
1836    rust_type: Boxed<'a, 'a, ParserInput<'a>, ParsedRustType<'a>, ZngParserExtra<'a>>,
1837) -> impl Parser<'a, ParserInput<'a>, ParsedRustTrait<'a>, ZngParserExtra<'a>> + Clone {
1838    let fn_trait = select! {
1839        Token::Ident(c) => c,
1840    }
1841    .then(fn_args(rust_type.clone()))
1842    .map(|x| ParsedRustTrait::Fn {
1843        name: x.0,
1844        inputs: x.1.0,
1845        output: Box::new(x.1.1),
1846    });
1847
1848    let rust_trait = fn_trait.or(rust_path_and_generics(rust_type).map(ParsedRustTrait::Normal));
1849    rust_trait.boxed()
1850}
1851
1852fn method<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedMethod<'a>, ZngParserExtra<'a>> + Clone {
1853    spanned(just(Token::KwAsync))
1854        .or_not()
1855        .then_ignore(just(Token::KwFn))
1856        .then(select! {
1857            Token::Ident(c) => c,
1858        })
1859        .then(
1860            rust_type()
1861                .separated_by(just(Token::Comma))
1862                .collect::<Vec<_>>()
1863                .delimited_by(just(Token::AngleOpen), just(Token::AngleClose))
1864                .or(empty().to(vec![]))
1865                .boxed(),
1866        )
1867        .then(fn_args(rust_type()))
1868        .map(|(((opt_async, name), generics), args)| {
1869            let is_self = |c: &ParsedRustType<'_>| {
1870                if let ParsedRustType::Adt(c) = c {
1871                    c.path.start == ParsedPathStart::Relative
1872                        && &c.path.segments == &["self"]
1873                        && c.generics.is_empty()
1874                } else {
1875                    false
1876                }
1877            };
1878            let (inputs, receiver) = match args.0.get(0) {
1879                Some(x) if is_self(&x) => (args.0[1..].to_vec(), ZngurMethodReceiver::Move),
1880                Some(ParsedRustType::Ref(m, x)) if is_self(&x) => {
1881                    (args.0[1..].to_vec(), ZngurMethodReceiver::Ref(*m))
1882                }
1883                _ => (args.0, ZngurMethodReceiver::Static),
1884            };
1885            let mut output = args.1;
1886            if let Some(async_kw) = opt_async {
1887                output = ParsedRustType::Impl(
1888                    ParsedRustTrait::Normal(ParsedRustPathAndGenerics {
1889                        path: ParsedPath {
1890                            start: ParsedPathStart::Absolute,
1891                            segments: vec!["std", "future", "Future"],
1892                            span: async_kw.span,
1893                        },
1894                        generics: vec![],
1895                        named_generics: vec![("Output", output)],
1896                    }),
1897                    vec![],
1898                )
1899            }
1900            ParsedMethod {
1901                name,
1902                receiver,
1903                generics,
1904                inputs,
1905                output,
1906            }
1907        })
1908        .boxed()
1909}
1910
1911fn inner_type_item<'a>()
1912-> impl Parser<'a, ParserInput<'a>, ParsedTypeItem<'a>, ZngParserExtra<'a>> + Clone {
1913    let property_item = (spanned(select! {
1914        Token::Ident(c) => c,
1915    }))
1916    .then_ignore(just(Token::Eq))
1917    .then(select! {
1918        Token::Number(c) => c,
1919    });
1920    let layout = just([Token::Sharp, Token::Ident("layout")])
1921        .ignore_then(
1922            property_item
1923                .clone()
1924                .separated_by(just(Token::Comma))
1925                .collect::<Vec<_>>()
1926                .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1927                .boxed(),
1928        )
1929        .map(ParsedLayoutPolicy::StackAllocated)
1930        .or(just([Token::Sharp, Token::Ident("layout_conservative")])
1931            .ignore_then(
1932                property_item
1933                    .clone()
1934                    .separated_by(just(Token::Comma))
1935                    .collect::<Vec<_>>()
1936                    .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1937                    .boxed(),
1938            )
1939            .map(ParsedLayoutPolicy::Conservative))
1940        .or(just([Token::Sharp, Token::Ident("only_by_ref")]).to(ParsedLayoutPolicy::OnlyByRef))
1941        .or(just([Token::Sharp, Token::Ident("heap_allocated")])
1942            .to(ParsedLayoutPolicy::HeapAllocated))
1943        .map_with(|x, extra| ParsedTypeItem::Layout(extra.span(), x))
1944        .boxed();
1945    let trait_item = select! {
1946        Token::Ident("Debug") => ZngurWellknownTrait::Debug,
1947        Token::Ident("Copy") => ZngurWellknownTrait::Copy,
1948    }
1949    .or(just(Token::Question)
1950        .then(just(Token::Ident("Sized")))
1951        .to(ZngurWellknownTrait::Unsized));
1952    let traits = just(Token::Ident("wellknown_traits"))
1953        .ignore_then(
1954            spanned(trait_item)
1955                .separated_by(just(Token::Comma))
1956                .collect::<Vec<_>>()
1957                .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1958                .boxed(),
1959        )
1960        .map(ParsedTypeItem::Traits)
1961        .boxed();
1962    let non_exhaustive =
1963        just(Token::Ident("non_exhaustive")).map(|_| ParsedTypeItem::NonExhaustive);
1964    let constructor_args = rust_type()
1965        .separated_by(just(Token::Comma))
1966        .collect::<Vec<_>>()
1967        .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1968        .map(ParsedConstructorArgs::Tuple)
1969        .or((select! {
1970            Token::Ident(c) => c,
1971        })
1972        .boxed()
1973        .then_ignore(just(Token::Colon))
1974        .then(rust_type())
1975        .separated_by(just(Token::Comma))
1976        .collect::<Vec<_>>()
1977        .delimited_by(just(Token::BraceOpen), just(Token::BraceClose))
1978        .map(ParsedConstructorArgs::Named))
1979        .or(empty().to(ParsedConstructorArgs::Unit))
1980        .boxed();
1981    let constructor = just(Token::Ident("constructor"))
1982        .ignore_then(constructor_args)
1983        .map(|args| ParsedTypeItem::Constructor { args });
1984    let field = just(Token::Ident("field")).ignore_then(
1985        (select! {
1986            Token::Ident(c) => c.to_owned(),
1987            Token::Number(c) => c.to_string(),
1988        })
1989        .then(
1990            just(Token::Ident("offset"))
1991                .then(just(Token::Eq))
1992                .ignore_then(select! {
1993                    Token::Number(c) => Some(c),
1994                    Token::Ident("auto") => None,
1995                })
1996                .then(
1997                    just(Token::Comma)
1998                        .then(just(Token::KwType))
1999                        .then(just(Token::Eq))
2000                        .ignore_then(rust_type()),
2001                )
2002                .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
2003                .boxed(),
2004        )
2005        .map(|(name, (offset, ty))| ParsedTypeItem::Field { name, ty, offset }),
2006    );
2007    let cpp_value = just(Token::Sharp)
2008        .then(just(Token::Ident("cpp_value")))
2009        .ignore_then(select! {
2010            Token::Str(c) => c,
2011        })
2012        .then(select! {
2013            Token::Str(c) => c,
2014        })
2015        .map(|x| ParsedTypeItem::CppValue {
2016            field: x.0,
2017            cpp_type: x.1,
2018        });
2019    let cpp_ref = just(Token::Sharp)
2020        .then(just(Token::Ident("cpp_ref")))
2021        .ignore_then(select! {
2022            Token::Str(c) => c,
2023        })
2024        .map(|x| ParsedTypeItem::CppRef { cpp_type: x });
2025    let cpp_stack_owned = just(Token::Sharp)
2026        .then(just(Token::Ident("cpp_stack_owned")))
2027        .ignore_then(select! {
2028            Token::Str(c) => c,
2029        })
2030        .then(
2031            property_item
2032                .clone()
2033                .separated_by(just(Token::Comma))
2034                .collect::<Vec<_>>()
2035                .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
2036                .boxed(),
2037        )
2038        .map(|(cpp_type, props)| ParsedTypeItem::CppStackOwned { cpp_type, props });
2039
2040    let variant = just(Token::Ident("variant"))
2041        .ignore_then(select! { Token::Ident(c) => c })
2042        .then(
2043            spanned(
2044                choice((non_exhaustive.clone(), field.clone())).then_ignore(just(Token::Semicolon)),
2045            )
2046            .repeated()
2047            .collect::<Vec<_>>()
2048            .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)),
2049        )
2050        .map(|(name, items)| ParsedTypeItem::Variant { name, items });
2051
2052    recursive(|item| {
2053        let inner_item = choice((
2054            layout.boxed(),
2055            traits.boxed(),
2056            non_exhaustive.boxed(),
2057            constructor.boxed(),
2058            field.boxed(),
2059            cpp_value.boxed(),
2060            cpp_ref.boxed(),
2061            cpp_stack_owned.boxed(),
2062            method()
2063                .then(
2064                    just(Token::KwUse)
2065                        .ignore_then(path())
2066                        .map(Some)
2067                        .or(empty().to(None))
2068                        .boxed(),
2069                )
2070                .then(
2071                    just(Token::Ident("deref"))
2072                        .ignore_then(rust_type())
2073                        .map(Some)
2074                        .or(empty().to(None))
2075                        .boxed(),
2076                )
2077                .then(
2078                    just(Token::KwAs)
2079                        .ignore_then(select! { Token::Ident(c) => Some(c), })
2080                        .or(empty().to(None))
2081                        .boxed(),
2082                )
2083                .map(
2084                    |(((data, use_path), deref), cpp_name)| ParsedTypeItem::Method {
2085                        deref,
2086                        use_path,
2087                        data,
2088                        cpp_name,
2089                    },
2090                )
2091                .boxed(),
2092        ));
2093
2094        let match_stmt = conditional_item::<_, CfgConditional<'a>, NItems>(item)
2095            .map(ParsedTypeItem::MatchOnCfg)
2096            .boxed();
2097
2098        choice((
2099            match_stmt,
2100            variant,
2101            inner_item.then_ignore(just(Token::Semicolon)).boxed(),
2102        ))
2103    })
2104    .boxed()
2105}
2106
2107fn type_item<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
2108    just(Token::KwType)
2109        .ignore_then(
2110            (select! { Token::Ident(c) => c })
2111                .map(ParsedTypeVar)
2112                .separated_by(just(Token::Comma))
2113                .at_least(1)
2114                .allow_trailing()
2115                .collect()
2116                .delimited_by(just(Token::AngleOpen), just(Token::AngleClose))
2117                .try_map_with(|vars, e: &mut MapExtra<_, ZngParserExtra>| {
2118                    if !e.state().unstable_features.template_types {
2119                        Err(Rich::custom(e.span(), "Template types are unstable. Enable them by using `#unstable(template_types)` at the top of the file."))
2120                    } else {
2121                        Ok(vars)
2122                    }
2123                })
2124                .or_not(),
2125        )
2126        .then(spanned(rust_type()))
2127        .then(
2128            spanned(inner_type_item())
2129                .repeated()
2130                .collect::<Vec<_>>()
2131                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)).boxed(),
2132        )
2133        .map(|((type_vars, ty), items)| ParsedItem::Type {
2134            ty,
2135            items,
2136            type_vars,
2137        })
2138        .boxed()
2139}
2140
2141fn trait_item<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone
2142{
2143    just(Token::KwTrait)
2144        .ignore_then(spanned(rust_trait(rust_type())))
2145        .then(
2146            method()
2147                .then_ignore(just(Token::Semicolon))
2148                .repeated()
2149                .collect::<Vec<_>>()
2150                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose))
2151                .boxed(),
2152        )
2153        .map(|(tr, methods)| ParsedItem::Trait { tr, methods })
2154        .boxed()
2155}
2156
2157fn fn_item<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
2158    spanned(method())
2159        .then_ignore(just(Token::Semicolon))
2160        .map(ParsedItem::Fn)
2161        .boxed()
2162}
2163
2164fn additional_include_item<'a>()
2165-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
2166    just(Token::Sharp)
2167        .ignore_then(choice((
2168            just(Token::Ident("cpp_additional_includes"))
2169                .ignore_then(select! {
2170                    Token::Str(c) => ParsedItem::CppAdditionalInclude(c),
2171                    Token::RawStr(_, c) => ParsedItem::CppAdditionalInclude(c),
2172                })
2173                .boxed(),
2174            just(Token::Ident("convert_panic_to_exception"))
2175                .map_with(|_, extra| ParsedItem::ConvertPanicToException(extra.span()))
2176                .boxed(),
2177        )))
2178        .boxed()
2179}
2180
2181fn extern_cpp_item<'a>()
2182-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
2183    let safety = choice((
2184        just(Token::KwSafe).to(true),
2185        just(Token::KwUnsafe).to(false),
2186    ));
2187    let function = safety
2188        .clone()
2189        .then(spanned(method()))
2190        .then_ignore(just(Token::Semicolon))
2191        .map(|(is_safe, method)| ParsedExternCppItem::Function { is_safe, method });
2192    let impl_block = just(Token::KwImpl)
2193        .ignore_then(
2194            rust_trait(rust_type())
2195                .then_ignore(just(Token::KwFor))
2196                .map(Some)
2197                .or(empty().to(None))
2198                .then(spanned(rust_type()))
2199                .boxed(),
2200        )
2201        .then(
2202            safety
2203                .then(method())
2204                .then_ignore(just(Token::Semicolon))
2205                .repeated()
2206                .collect::<Vec<_>>()
2207                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose))
2208                .boxed(),
2209        )
2210        .map(|((tr, ty), methods)| ParsedExternCppItem::Impl { tr, ty, methods });
2211    just(Token::KwExtern)
2212        .then(just(Token::Str("C++")))
2213        .ignore_then(
2214            function
2215                .or(impl_block)
2216                .repeated()
2217                .collect::<Vec<_>>()
2218                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose))
2219                .boxed(),
2220        )
2221        .map(ParsedItem::ExternCpp)
2222        .boxed()
2223}
2224
2225fn unstable_feature<'a>()
2226-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
2227    just([Token::Sharp, Token::Ident("unstable")])
2228        .ignore_then(
2229            select! { Token::Ident(feat) => feat }
2230                .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
2231                .try_map_with(|feat, e| match feat {
2232                    "cfg_match" => {
2233                        let ctx: &mut extra::SimpleState<ZngParserState> = e.state();
2234                        ctx.unstable_features.cfg_match = true;
2235                        Ok(ParsedItem::UnstableFeature("cfg_match"))
2236                    }
2237                    "cfg_if" => {
2238                        let ctx: &mut extra::SimpleState<ZngParserState> = e.state();
2239                        ctx.unstable_features.cfg_if = true;
2240                        Ok(ParsedItem::UnstableFeature("cfg_if"))
2241                    }
2242                    "template_types" => {
2243                        let ctx: &mut extra::SimpleState<ZngParserState> = e.state();
2244                        ctx.unstable_features.template_types = true;
2245                        Ok(ParsedItem::UnstableFeature("template_types"))
2246                    }
2247                    _ => Err(Rich::custom(
2248                        e.span(),
2249                        format!("unknown unstable feature '{feat}'"),
2250                    )),
2251                }),
2252        )
2253        .boxed()
2254}
2255
2256fn item<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
2257    recursive(|item| {
2258        choice((
2259            unstable_feature(),
2260            just(Token::KwMod)
2261                .ignore_then(path())
2262                .then(
2263                    item.clone()
2264                        .repeated()
2265                        .collect::<Vec<_>>()
2266                        .delimited_by(just(Token::BraceOpen), just(Token::BraceClose))
2267                        .boxed(),
2268                )
2269                .map(|(path, items)| ParsedItem::Mod { path, items })
2270                .boxed(),
2271            type_item(),
2272            trait_item(),
2273            extern_cpp_item(),
2274            fn_item(),
2275            additional_include_item(),
2276            import_item(),
2277            module_import_item(),
2278            alias(),
2279            conditional_item::<_, CfgConditional<'a>, NItems>(item).map(ParsedItem::MatchOnCfg),
2280        ))
2281    })
2282    .boxed()
2283}
2284
2285fn import_item<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone
2286{
2287    just(Token::KwMerge)
2288        .ignore_then(select! {
2289            Token::Str(path) => path,
2290        })
2291        .then_ignore(just(Token::Semicolon))
2292        .map_with(|path, extra| {
2293            ParsedItem::Import(ParsedImportPath {
2294                path: std::path::PathBuf::from(path),
2295                span: extra.span(),
2296            })
2297        })
2298        .boxed()
2299}
2300
2301fn module_import_item<'a>()
2302-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, ZngParserExtra<'a>> + Clone {
2303    just(Token::KwImport)
2304        .ignore_then(select! { Token::Str(path) => path })
2305        .then_ignore(just(Token::Semicolon))
2306        .map_with(|path, extra| ParsedItem::ModuleImport {
2307            path: std::path::PathBuf::from(path),
2308            span: extra.span(),
2309        })
2310        .boxed()
2311}
2312
2313fn path<'a>() -> impl Parser<'a, ParserInput<'a>, ParsedPath<'a>, ZngParserExtra<'a>> + Clone {
2314    let start = choice((
2315        just(Token::ColonColon).to(ParsedPathStart::Absolute),
2316        just(Token::KwCrate)
2317            .then(just(Token::ColonColon))
2318            .to(ParsedPathStart::Crate),
2319        empty().to(ParsedPathStart::Relative),
2320    ));
2321
2322    start
2323        .then(
2324            (select! {
2325                Token::Ident(c) => c,
2326            })
2327            .separated_by(just(Token::ColonColon))
2328            .at_least(1)
2329            .collect::<Vec<_>>()
2330            .boxed(),
2331        )
2332        .or(just(Token::KwCrate).to((ParsedPathStart::Crate, vec![])))
2333        .map_with(|(start, segments), extra| ParsedPath {
2334            start,
2335            segments,
2336            span: extra.span(),
2337        })
2338        .boxed()
2339}
2340
2341impl<'a> conditional::BodyItem for crate::ParsedTypeItem<'a> {
2342    type Processed = Self;
2343
2344    fn process(self, _ctx: &mut ParseContext) -> Self::Processed {
2345        self
2346    }
2347}
2348
2349impl<'a> conditional::BodyItem for crate::ParsedItem<'a> {
2350    type Processed = ProcessedItemOrAlias<'a>;
2351
2352    fn process(self, ctx: &mut ParseContext) -> Self::Processed {
2353        crate::process_parsed_item(self, ctx)
2354    }
2355}