zngur_parser/
lib.rs

1use std::{fmt::Display, path::Component, process::exit};
2
3use ariadne::{Color, Label, Report, ReportKind, sources};
4use chumsky::prelude::*;
5use itertools::{Either, Itertools};
6
7use zngur_def::{
8    AdditionalIncludes, ConvertPanicToException, CppRef, CppValue, Import, LayoutPolicy, Merge,
9    MergeFailure, Mutability, PrimitiveRustType, RustPathAndGenerics, RustTrait, RustType,
10    ZngurConstructor, ZngurExternCppFn, ZngurExternCppImpl, ZngurField, ZngurFn, ZngurMethod,
11    ZngurMethodDetails, ZngurMethodReceiver, ZngurSpec, ZngurTrait, ZngurType, ZngurWellknownTrait,
12};
13
14pub type Span = SimpleSpan<usize>;
15
16#[cfg(test)]
17mod tests;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20struct Spanned<T> {
21    inner: T,
22    span: Span,
23}
24
25type ParserInput<'a> = chumsky::input::MappedInput<
26    Token<'a>,
27    Span,
28    &'a [(Token<'a>, Span)],
29    Box<
30        dyn for<'x> Fn(
31            &'x (Token<'_>, chumsky::span::SimpleSpan),
32        ) -> (&'x Token<'x>, &'x SimpleSpan),
33    >,
34>;
35
36#[derive(Debug)]
37pub struct ParsedZngFile<'a>(Vec<ParsedItem<'a>>);
38
39#[derive(Debug)]
40pub struct ProcessedZngFile<'a> {
41    aliases: Vec<ParsedAlias<'a>>,
42    items: Vec<ProcessedItem<'a>>,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46enum ParsedPathStart {
47    Absolute,
48    Relative,
49    Crate,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53struct ParsedPath<'a> {
54    start: ParsedPathStart,
55    segments: Vec<&'a str>,
56    span: Span,
57}
58
59impl ParsedPath<'_> {
60    fn to_zngur(self, base: &[String]) -> Vec<String> {
61        match self.start {
62            ParsedPathStart::Absolute => self.segments.into_iter().map(|x| x.to_owned()).collect(),
63            ParsedPathStart::Relative => base
64                .iter()
65                .map(|x| x.as_str())
66                .chain(self.segments)
67                .map(|x| x.to_owned())
68                .collect(),
69            ParsedPathStart::Crate => ["crate"]
70                .into_iter()
71                .chain(self.segments)
72                .map(|x| x.to_owned())
73                .collect(),
74        }
75    }
76
77    fn matches_alias(&self, alias: &ParsedAlias<'_>) -> bool {
78        match self.start {
79            ParsedPathStart::Absolute | ParsedPathStart::Crate => false,
80            ParsedPathStart::Relative => self
81                .segments
82                .first()
83                .is_some_and(|part| *part == alias.name),
84        }
85    }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
89pub struct ParsedAlias<'a> {
90    name: &'a str,
91    path: ParsedPath<'a>,
92    span: Span,
93}
94
95impl ParsedAlias<'_> {
96    fn expand(&self, path: &ParsedPath<'_>, base: &[String]) -> Option<Vec<String>> {
97        if path.matches_alias(self) {
98            match self.path.start {
99                ParsedPathStart::Absolute => Some(
100                    self.path
101                        .segments
102                        .iter()
103                        .chain(path.segments.iter().skip(1))
104                        .map(|seg| (*seg).to_owned())
105                        .collect(),
106                ),
107                ParsedPathStart::Crate => Some(
108                    ["crate"]
109                        .into_iter()
110                        .chain(self.path.segments.iter().cloned())
111                        .chain(path.segments.iter().skip(1).cloned())
112                        .map(|seg| (*seg).to_owned())
113                        .collect(),
114                ),
115                ParsedPathStart::Relative => Some(
116                    base.iter()
117                        .map(|x| x.as_str())
118                        .chain(self.path.segments.iter().cloned())
119                        .chain(path.segments.iter().skip(1).cloned())
120                        .map(|seg| (*seg).to_owned())
121                        .collect(),
122                ),
123            }
124        } else {
125            None
126        }
127    }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
131struct ParsedImportPath {
132    path: std::path::PathBuf,
133    span: Span,
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
137enum ParsedItem<'a> {
138    ConvertPanicToException(Span),
139    CppAdditionalInclude(&'a str),
140    Mod {
141        path: ParsedPath<'a>,
142        items: Vec<ParsedItem<'a>>,
143    },
144    Type {
145        ty: Spanned<ParsedRustType<'a>>,
146        items: Vec<Spanned<ParsedTypeItem<'a>>>,
147    },
148    Trait {
149        tr: Spanned<ParsedRustTrait<'a>>,
150        methods: Vec<ParsedMethod<'a>>,
151    },
152    Fn(Spanned<ParsedMethod<'a>>),
153    ExternCpp(Vec<ParsedExternCppItem<'a>>),
154    Alias(ParsedAlias<'a>),
155    Import(ParsedImportPath),
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
159enum ProcessedItem<'a> {
160    ConvertPanicToException(Span),
161    CppAdditionalInclude(&'a str),
162    Mod {
163        path: ParsedPath<'a>,
164        items: Vec<ProcessedItem<'a>>,
165        aliases: Vec<ParsedAlias<'a>>,
166    },
167    Type {
168        ty: Spanned<ParsedRustType<'a>>,
169        items: Vec<Spanned<ParsedTypeItem<'a>>>,
170    },
171    Trait {
172        tr: Spanned<ParsedRustTrait<'a>>,
173        methods: Vec<ParsedMethod<'a>>,
174    },
175    Fn(Spanned<ParsedMethod<'a>>),
176    ExternCpp(Vec<ParsedExternCppItem<'a>>),
177    Import(ParsedImportPath),
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
181enum ParsedExternCppItem<'a> {
182    Function(Spanned<ParsedMethod<'a>>),
183    Impl {
184        tr: Option<ParsedRustTrait<'a>>,
185        ty: Spanned<ParsedRustType<'a>>,
186        methods: Vec<ParsedMethod<'a>>,
187    },
188}
189
190#[derive(Debug, Clone, PartialEq, Eq)]
191enum ParsedConstructorArgs<'a> {
192    Unit,
193    Tuple(Vec<ParsedRustType<'a>>),
194    Named(Vec<(&'a str, ParsedRustType<'a>)>),
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
198enum ParsedLayoutPolicy<'a> {
199    StackAllocated(Vec<(Spanned<&'a str>, usize)>),
200    HeapAllocated,
201    OnlyByRef,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
205enum ParsedTypeItem<'a> {
206    Layout(Span, ParsedLayoutPolicy<'a>),
207    Traits(Vec<Spanned<ZngurWellknownTrait>>),
208    Constructor {
209        name: Option<&'a str>,
210        args: ParsedConstructorArgs<'a>,
211    },
212    Field {
213        name: String,
214        ty: ParsedRustType<'a>,
215        offset: usize,
216    },
217    Method {
218        data: ParsedMethod<'a>,
219        use_path: Option<ParsedPath<'a>>,
220        deref: Option<ParsedRustType<'a>>,
221    },
222    CppValue {
223        field: &'a str,
224        cpp_type: &'a str,
225    },
226    CppRef {
227        cpp_type: &'a str,
228    },
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
232struct ParsedMethod<'a> {
233    name: &'a str,
234    receiver: ZngurMethodReceiver,
235    generics: Vec<ParsedRustType<'a>>,
236    inputs: Vec<ParsedRustType<'a>>,
237    output: ParsedRustType<'a>,
238}
239
240impl ParsedMethod<'_> {
241    fn to_zngur(self, aliases: &[ParsedAlias<'_>], base: &[String]) -> ZngurMethod {
242        ZngurMethod {
243            name: self.name.to_owned(),
244            generics: self
245                .generics
246                .into_iter()
247                .map(|x| x.to_zngur(aliases, base))
248                .collect(),
249            receiver: self.receiver,
250            inputs: self
251                .inputs
252                .into_iter()
253                .map(|x| x.to_zngur(aliases, base))
254                .collect(),
255            output: self.output.to_zngur(aliases, base),
256        }
257    }
258}
259
260fn checked_merge<T, U>(src: T, dst: &mut U, span: Span, ctx: &ParseContext)
261where
262    T: Merge<U>,
263{
264    match src.merge(dst) {
265        Ok(()) => {}
266        Err(e) => match e {
267            MergeFailure::Conflict(s) => {
268                create_and_emit_error(ctx, &s, span);
269            }
270        },
271    }
272}
273
274impl ProcessedItem<'_> {
275    fn add_to_zngur_spec(
276        self,
277        r: &mut ZngurSpec,
278        aliases: &[ParsedAlias],
279        base: &[String],
280        ctx: &ParseContext,
281    ) {
282        match self {
283            ProcessedItem::Mod {
284                path,
285                items,
286                aliases: mut mod_aliases,
287            } => {
288                let base = path.to_zngur(base);
289                mod_aliases.extend_from_slice(aliases);
290                for item in items {
291                    item.add_to_zngur_spec(r, &mod_aliases, &base, ctx);
292                }
293            }
294            ProcessedItem::Import(path) => {
295                if path.path.is_absolute() {
296                    create_and_emit_error(
297                        ctx,
298                        "Absolute paths imports are not supported.",
299                        path.span,
300                    )
301                }
302                match path.path.components().next() {
303                    Some(Component::CurDir) | Some(Component::ParentDir) => {}
304                    _ => create_and_emit_error(
305                        ctx,
306                        "Module import is not supported. Use a relative path instead.",
307                        path.span,
308                    ),
309                }
310
311                r.imports.push(Import(path.path));
312            }
313            ProcessedItem::Type { ty, items } => {
314                if ty.inner == ParsedRustType::Tuple(vec![]) {
315                    // We add unit type implicitly.
316                    create_and_emit_error(
317                        ctx,
318                        "Unit type is declared implicitly. Remove this entirely.",
319                        ty.span,
320                    );
321                }
322
323                let mut methods = vec![];
324                let mut constructors = vec![];
325                let mut fields = vec![];
326                let mut wellknown_traits = vec![];
327                let mut layout = None;
328                let mut layout_span = None;
329                let mut cpp_value = None;
330                let mut cpp_ref = None;
331                for item in items {
332                    let item_span = item.span;
333                    let item = item.inner;
334                    match item {
335                        ParsedTypeItem::Layout(span, p) => {
336                            layout = Some(match p {
337                                ParsedLayoutPolicy::StackAllocated(p) => {
338                                    let mut size = None;
339                                    let mut align = None;
340                                    for (key, value) in p {
341                                        match key.inner {
342                                            "size" => size = Some(value),
343                                            "align" => align = Some(value),
344                                            _ => create_and_emit_error(
345                                                ctx,
346                                                "Unknown property",
347                                                key.span,
348                                            ),
349                                        }
350                                    }
351                                    let Some(size) = size else {
352                                        create_and_emit_error(
353                                            ctx,
354                                            "Size is not declared for this type",
355                                            ty.span,
356                                        );
357                                    };
358                                    let Some(align) = align else {
359                                        create_and_emit_error(
360                                            ctx,
361                                            "Align is not declared for this type",
362                                            ty.span,
363                                        );
364                                    };
365                                    LayoutPolicy::StackAllocated { size, align }
366                                }
367                                ParsedLayoutPolicy::HeapAllocated => LayoutPolicy::HeapAllocated,
368                                ParsedLayoutPolicy::OnlyByRef => LayoutPolicy::OnlyByRef,
369                            });
370                            match layout_span {
371                                Some(_) => {
372                                    create_and_emit_error(
373                                        ctx,
374                                        "Duplicate layout policy found",
375                                        span,
376                                    );
377                                }
378                                None => layout_span = Some(span),
379                            }
380                        }
381                        ParsedTypeItem::Traits(tr) => {
382                            wellknown_traits.extend(tr);
383                        }
384                        ParsedTypeItem::Constructor { name, args } => {
385                            constructors.push(ZngurConstructor {
386                                name: name.map(|x| x.to_owned()),
387                                inputs: match args {
388                                    ParsedConstructorArgs::Unit => vec![],
389                                    ParsedConstructorArgs::Tuple(t) => t
390                                        .into_iter()
391                                        .enumerate()
392                                        .map(|(i, t)| (i.to_string(), t.to_zngur(aliases, base)))
393                                        .collect(),
394                                    ParsedConstructorArgs::Named(t) => t
395                                        .into_iter()
396                                        .map(|(i, t)| (i.to_owned(), t.to_zngur(aliases, base)))
397                                        .collect(),
398                                },
399                            })
400                        }
401                        ParsedTypeItem::Field { name, ty, offset } => {
402                            fields.push(ZngurField {
403                                name: name.to_owned(),
404                                ty: ty.to_zngur(aliases, base),
405                                offset,
406                            });
407                        }
408                        ParsedTypeItem::Method {
409                            data,
410                            use_path,
411                            deref,
412                        } => {
413                            methods.push(ZngurMethodDetails {
414                                data: data.to_zngur(aliases, base),
415                                use_path: use_path.map(|x| {
416                                    aliases
417                                        .iter()
418                                        .filter_map(|alias| alias.expand(&x, base))
419                                        .collect::<Vec<_>>()
420                                        .first()
421                                        .cloned()
422                                        .unwrap_or_else(|| x.to_zngur(base))
423                                }),
424                                deref: deref.map(|x| x.to_zngur(aliases, base)),
425                            });
426                        }
427                        ParsedTypeItem::CppValue { field, cpp_type } => {
428                            cpp_value = Some(CppValue(field.to_owned(), cpp_type.to_owned()));
429                        }
430                        ParsedTypeItem::CppRef { cpp_type } => {
431                            match layout_span {
432                                Some(span) => {
433                                    create_and_emit_error(
434                                        ctx,
435                                        "Duplicate layout policy found",
436                                        span,
437                                    );
438                                }
439                                None => {
440                                    layout = Some(LayoutPolicy::ZERO_SIZED_TYPE);
441                                    layout_span = Some(item_span);
442                                }
443                            }
444                            cpp_ref = Some(CppRef(cpp_type.to_owned()));
445                        }
446                    }
447                }
448                let is_unsized = wellknown_traits
449                    .iter()
450                    .find(|x| x.inner == ZngurWellknownTrait::Unsized)
451                    .cloned();
452                let is_copy = wellknown_traits
453                    .iter()
454                    .find(|x| x.inner == ZngurWellknownTrait::Copy)
455                    .cloned();
456                let mut wt = wellknown_traits
457                    .into_iter()
458                    .map(|x| x.inner)
459                    .collect::<Vec<_>>();
460                if is_copy.is_none() && is_unsized.is_none() {
461                    wt.push(ZngurWellknownTrait::Drop);
462                }
463                if let Some(is_unsized) = is_unsized {
464                    if let Some(span) = layout_span {
465                        emit_ariadne_error(
466                            ctx,
467                            Report::build(
468                                ReportKind::Error,
469                                ctx.filename().to_string(),
470                                span.start,
471                            )
472                            .with_message("Duplicate layout policy found for unsized type.")
473                            .with_label(
474                                Label::new((ctx.filename().to_string(), span.start..span.end))
475                                    .with_message(
476                                        "Unsized types have implicit layout policy, remove this.",
477                                    )
478                                    .with_color(Color::Red),
479                            )
480                            .with_label(
481                                Label::new((
482                                    ctx.filename().to_string(),
483                                    is_unsized.span.start..is_unsized.span.end,
484                                ))
485                                .with_message("Type declared as unsized here.")
486                                .with_color(Color::Blue),
487                            )
488                            .finish(),
489                        )
490                    }
491                    layout = Some(LayoutPolicy::OnlyByRef);
492                }
493                let Some(layout) = layout else {
494                    create_and_emit_error(
495                        ctx,
496                        "No layout policy found for this type. \
497Use one of `#layout(size = X, align = Y)`, `#heap_allocated` or `#only_by_ref`.",
498                        ty.span,
499                    );
500                };
501                checked_merge(
502                    ZngurType {
503                        ty: ty.inner.to_zngur(aliases, base),
504                        layout,
505                        methods,
506                        wellknown_traits: wt,
507                        constructors,
508                        fields,
509                        cpp_value,
510                        cpp_ref,
511                    },
512                    r,
513                    ty.span,
514                    ctx,
515                );
516            }
517            ProcessedItem::Trait { tr, methods } => {
518                checked_merge(
519                    ZngurTrait {
520                        tr: tr.inner.to_zngur(aliases, base),
521                        methods: methods
522                            .into_iter()
523                            .map(|m| m.to_zngur(aliases, base))
524                            .collect(),
525                    },
526                    r,
527                    tr.span,
528                    ctx,
529                );
530            }
531            ProcessedItem::Fn(f) => {
532                let method = f.inner.to_zngur(aliases, base);
533                checked_merge(
534                    ZngurFn {
535                        path: RustPathAndGenerics {
536                            path: base.iter().chain(Some(&method.name)).cloned().collect(),
537                            generics: method.generics,
538                            named_generics: vec![],
539                        },
540                        inputs: method.inputs,
541                        output: method.output,
542                    },
543                    r,
544                    f.span,
545                    ctx,
546                );
547            }
548            ProcessedItem::ExternCpp(items) => {
549                for item in items {
550                    match item {
551                        ParsedExternCppItem::Function(method) => {
552                            let span = method.span;
553                            let method = method.inner.to_zngur(aliases, base);
554                            checked_merge(
555                                ZngurExternCppFn {
556                                    name: method.name.to_string(),
557                                    inputs: method.inputs,
558                                    output: method.output,
559                                },
560                                r,
561                                span,
562                                ctx,
563                            );
564                        }
565                        ParsedExternCppItem::Impl { tr, ty, methods } => {
566                            checked_merge(
567                                ZngurExternCppImpl {
568                                    tr: tr.map(|x| x.to_zngur(aliases, base)),
569                                    ty: ty.inner.to_zngur(aliases, base),
570                                    methods: methods
571                                        .into_iter()
572                                        .map(|x| x.to_zngur(aliases, base))
573                                        .collect(),
574                                },
575                                r,
576                                ty.span,
577                                ctx,
578                            );
579                        }
580                    }
581                }
582            }
583            ProcessedItem::CppAdditionalInclude(s) => {
584                match AdditionalIncludes(s.to_owned()).merge(r) {
585                    Ok(()) => {}
586                    Err(_) => {
587                        unreachable!() // For now, additional includes can't have conflicts.
588                    }
589                }
590            }
591            ProcessedItem::ConvertPanicToException(span) => {
592                if ctx.depth > 0 {
593                    create_and_emit_error(
594                        ctx,
595                        "Using `#convert_panic_to_exception` in imported zngur files is not supported. This directive can only be used in the main zngur file.",
596                        span,
597                    );
598                }
599                match ConvertPanicToException(true).merge(r) {
600                    Ok(()) => {}
601                    Err(_) => {
602                        unreachable!() // For now, CPtE also can't have conflicts.
603                    }
604                }
605            }
606        }
607    }
608}
609
610#[derive(Debug, Clone, PartialEq, Eq)]
611enum ParsedRustType<'a> {
612    Primitive(PrimitiveRustType),
613    Ref(Mutability, Box<ParsedRustType<'a>>),
614    Raw(Mutability, Box<ParsedRustType<'a>>),
615    Boxed(Box<ParsedRustType<'a>>),
616    Slice(Box<ParsedRustType<'a>>),
617    Dyn(ParsedRustTrait<'a>, Vec<&'a str>),
618    Tuple(Vec<ParsedRustType<'a>>),
619    Adt(ParsedRustPathAndGenerics<'a>),
620}
621
622impl ParsedRustType<'_> {
623    fn to_zngur(self, aliases: &[ParsedAlias<'_>], base: &[String]) -> RustType {
624        match self {
625            ParsedRustType::Primitive(s) => RustType::Primitive(s),
626            ParsedRustType::Ref(m, s) => RustType::Ref(m, Box::new(s.to_zngur(aliases, base))),
627            ParsedRustType::Raw(m, s) => RustType::Raw(m, Box::new(s.to_zngur(aliases, base))),
628            ParsedRustType::Boxed(s) => RustType::Boxed(Box::new(s.to_zngur(aliases, base))),
629            ParsedRustType::Slice(s) => RustType::Slice(Box::new(s.to_zngur(aliases, base))),
630            ParsedRustType::Dyn(tr, bounds) => RustType::Dyn(
631                tr.to_zngur(aliases, base),
632                bounds.into_iter().map(|x| x.to_owned()).collect(),
633            ),
634            ParsedRustType::Tuple(v) => {
635                RustType::Tuple(v.into_iter().map(|s| s.to_zngur(aliases, base)).collect())
636            }
637            ParsedRustType::Adt(s) => RustType::Adt(s.to_zngur(aliases, base)),
638        }
639    }
640}
641
642#[derive(Debug, Clone, PartialEq, Eq)]
643enum ParsedRustTrait<'a> {
644    Normal(ParsedRustPathAndGenerics<'a>),
645    Fn {
646        name: &'a str,
647        inputs: Vec<ParsedRustType<'a>>,
648        output: Box<ParsedRustType<'a>>,
649    },
650}
651
652impl ParsedRustTrait<'_> {
653    fn to_zngur(self, aliases: &[ParsedAlias<'_>], base: &[String]) -> RustTrait {
654        match self {
655            ParsedRustTrait::Normal(s) => RustTrait::Normal(s.to_zngur(aliases, base)),
656            ParsedRustTrait::Fn {
657                name,
658                inputs,
659                output,
660            } => RustTrait::Fn {
661                name: name.to_owned(),
662                inputs: inputs
663                    .into_iter()
664                    .map(|s| s.to_zngur(aliases, base))
665                    .collect(),
666                output: Box::new(output.to_zngur(aliases, base)),
667            },
668        }
669    }
670}
671
672#[derive(Debug, Clone, PartialEq, Eq)]
673struct ParsedRustPathAndGenerics<'a> {
674    path: ParsedPath<'a>,
675    generics: Vec<ParsedRustType<'a>>,
676    named_generics: Vec<(&'a str, ParsedRustType<'a>)>,
677}
678
679impl ParsedRustPathAndGenerics<'_> {
680    fn to_zngur(self, aliases: &[ParsedAlias<'_>], base: &[String]) -> RustPathAndGenerics {
681        RustPathAndGenerics {
682            path: aliases
683                .iter()
684                .filter_map(|alias| alias.expand(&self.path, base))
685                .collect::<Vec<_>>()
686                .first()
687                .cloned()
688                .unwrap_or_else(|| self.path.to_zngur(base)),
689            generics: self
690                .generics
691                .into_iter()
692                .map(|x| x.to_zngur(aliases, base))
693                .collect(),
694            named_generics: self
695                .named_generics
696                .into_iter()
697                .map(|(name, x)| (name.to_owned(), x.to_zngur(aliases, base)))
698                .collect(),
699        }
700    }
701}
702
703struct ParseContext<'a> {
704    path: std::path::PathBuf,
705    text: &'a str,
706    depth: usize,
707}
708
709impl<'a> ParseContext<'a> {
710    fn new(path: std::path::PathBuf, text: &'a str) -> Self {
711        Self {
712            path,
713            text,
714            depth: 0,
715        }
716    }
717
718    fn with_depth(path: std::path::PathBuf, text: &'a str, depth: usize) -> Self {
719        Self { path, text, depth }
720    }
721
722    fn filename(&self) -> &str {
723        self.path.file_name().unwrap().to_str().unwrap()
724    }
725}
726
727/// A trait for types which can resolve filesystem-like paths relative to a given directory.
728pub trait ImportResolver {
729    fn resolve_import(
730        &self,
731        cwd: &std::path::Path,
732        relpath: &std::path::Path,
733    ) -> Result<String, String>;
734}
735
736/// A default implementation of ImportResolver which uses conventional filesystem paths and semantics.
737struct DefaultImportResolver;
738
739impl ImportResolver for DefaultImportResolver {
740    fn resolve_import(
741        &self,
742        cwd: &std::path::Path,
743        relpath: &std::path::Path,
744    ) -> Result<String, String> {
745        let path = cwd
746            .join(relpath)
747            .canonicalize()
748            .map_err(|e| e.to_string())?;
749        std::fs::read_to_string(path).map_err(|e| e.to_string())
750    }
751}
752
753impl<'a> ParsedZngFile<'a> {
754    fn parse_into(zngur: &mut ZngurSpec, ctx: &ParseContext, resolver: &impl ImportResolver) {
755        let (tokens, errs) = lexer().parse(ctx.text).into_output_errors();
756        let Some(tokens) = tokens else {
757            let errs = errs.into_iter().map(|e| e.map_token(|c| c.to_string()));
758            emit_error(&ctx, errs);
759        };
760        let tokens: ParserInput<'_> = tokens.as_slice().map(
761            (ctx.text.len()..ctx.text.len()).into(),
762            Box::new(|(t, s)| (t, s)),
763        );
764        let (ast, errs) = file_parser()
765            .map_with(|ast, extra| (ast, extra.span()))
766            .parse(tokens)
767            .into_output_errors();
768        let Some(ast) = ast else {
769            let errs = errs.into_iter().map(|e| e.map_token(|c| c.to_string()));
770            emit_error(&ctx, errs);
771        };
772
773        let (aliases, items) = ast.0.0.into_iter().partition_map(partition_parsed_item_vec);
774        ProcessedZngFile::new(aliases, items).into_zngur_spec(zngur, &ctx);
775
776        if let Some(dirname) = ctx.path.parent() {
777            for import in std::mem::take(&mut zngur.imports) {
778                match resolver.resolve_import(dirname, &import.0) {
779                    Ok(text) => {
780                        Self::parse_into(
781                            zngur,
782                            &ParseContext::with_depth(
783                                dirname.join(&import.0),
784                                &text,
785                                ctx.depth + 1,
786                            ),
787                            resolver,
788                        );
789                    }
790                    Err(_) => {
791                        // TODO: emit a better error. How should we get a span here?
792                        // I'd like to avoid putting a ParsedImportPath in ZngurSpec, and
793                        // also not have to pass a filename to add_to_zngur_spec.
794                        emit_ariadne_error(
795                            &ctx,
796                            Report::build(ReportKind::Error, ctx.filename(), 0)
797                                .with_message(format!(
798                                    "Import path not found: {}",
799                                    import.0.display()
800                                ))
801                                .finish(),
802                        );
803                    }
804                }
805            }
806        }
807    }
808
809    pub fn parse(path: std::path::PathBuf) -> ZngurSpec {
810        let mut zngur = ZngurSpec::default();
811        let text = std::fs::read_to_string(&path).unwrap();
812        Self::parse_into(
813            &mut zngur,
814            &ParseContext::new(path, &text),
815            &DefaultImportResolver,
816        );
817        zngur
818    }
819
820    pub fn parse_str(text: &str) -> ZngurSpec {
821        let mut zngur = ZngurSpec::default();
822        Self::parse_into(
823            &mut zngur,
824            &ParseContext::new(std::path::PathBuf::from("test.zng"), text),
825            &DefaultImportResolver,
826        );
827        zngur
828    }
829
830    #[cfg(test)]
831    pub(crate) fn parse_str_with_resolver(text: &str, resolver: &impl ImportResolver) -> ZngurSpec {
832        let mut zngur = ZngurSpec::default();
833        Self::parse_into(
834            &mut zngur,
835            &ParseContext::new(std::path::PathBuf::from("test.zng"), text),
836            resolver,
837        );
838        zngur
839    }
840}
841
842fn partition_parsed_item_vec(item: ParsedItem<'_>) -> Either<ParsedAlias<'_>, ProcessedItem<'_>> {
843    match item {
844        ParsedItem::Alias(alias) => Either::Left(alias),
845        ParsedItem::ConvertPanicToException(span) => {
846            Either::Right(ProcessedItem::ConvertPanicToException(span))
847        }
848        ParsedItem::CppAdditionalInclude(inc) => {
849            Either::Right(ProcessedItem::CppAdditionalInclude(inc))
850        }
851        ParsedItem::Mod { path, items } => {
852            let (aliases, items): (Vec<ParsedAlias<'_>>, Vec<ProcessedItem<'_>>) =
853                items.into_iter().partition_map(partition_parsed_item_vec);
854            Either::Right(ProcessedItem::Mod {
855                path,
856                items,
857                aliases,
858            })
859        }
860        ParsedItem::Type { ty, items } => Either::Right(ProcessedItem::Type { ty, items }),
861        ParsedItem::Trait { tr, methods } => Either::Right(ProcessedItem::Trait { tr, methods }),
862        ParsedItem::Fn(method) => Either::Right(ProcessedItem::Fn(method)),
863        ParsedItem::ExternCpp(items) => Either::Right(ProcessedItem::ExternCpp(items)),
864        ParsedItem::Import(path) => Either::Right(ProcessedItem::Import(path)),
865    }
866}
867
868impl<'a> ProcessedZngFile<'a> {
869    fn new(aliases: Vec<ParsedAlias<'a>>, items: Vec<ProcessedItem<'a>>) -> Self {
870        ProcessedZngFile { aliases, items }
871    }
872
873    fn into_zngur_spec(self, zngur: &mut ZngurSpec, ctx: &ParseContext) {
874        for item in self.items {
875            item.add_to_zngur_spec(zngur, &self.aliases, &[], ctx);
876        }
877    }
878}
879
880fn create_and_emit_error(ctx: &ParseContext, error: &str, span: Span) -> ! {
881    emit_error(ctx, [Rich::custom(span, error)].into_iter())
882}
883
884#[cfg(test)]
885fn emit_ariadne_error(ctx: &ParseContext, err: Report<'_, (String, std::ops::Range<usize>)>) -> ! {
886    let mut r = Vec::<u8>::new();
887    err.write(sources([(ctx.filename().to_string(), ctx.text)]), &mut r)
888        .unwrap();
889
890    std::panic::resume_unwind(Box::new(tests::ErrorText(
891        String::from_utf8(strip_ansi_escapes::strip(r)).unwrap(),
892    )));
893}
894
895#[cfg(not(test))]
896fn emit_ariadne_error(ctx: &ParseContext, err: Report<'_, (String, std::ops::Range<usize>)>) -> ! {
897    err.eprint(sources([(ctx.filename().to_string(), ctx.text)]))
898        .unwrap();
899    exit(101);
900}
901
902fn emit_error<'a>(ctx: &ParseContext, errs: impl Iterator<Item = Rich<'a, String>>) -> ! {
903    for e in errs {
904        emit_ariadne_error(
905            ctx,
906            Report::build(ReportKind::Error, ctx.filename(), e.span().start)
907                .with_message(e.to_string())
908                .with_label(
909                    Label::new((ctx.filename().to_string(), e.span().into_range()))
910                        .with_message(e.reason().to_string())
911                        .with_color(Color::Red),
912                )
913                .with_labels(e.contexts().map(|(label, span)| {
914                    Label::new((ctx.filename().to_string(), span.into_range()))
915                        .with_message(format!("while parsing this {}", label))
916                        .with_color(Color::Yellow)
917                }))
918                .finish(),
919        )
920    }
921    exit(101);
922}
923
924#[derive(Debug, Clone, PartialEq, Eq, Hash)]
925enum Token<'a> {
926    Arrow,
927    AngleOpen,
928    AngleClose,
929    BracketOpen,
930    BracketClose,
931    Colon,
932    ColonColon,
933    ParenOpen,
934    ParenClose,
935    BraceOpen,
936    BraceClose,
937    And,
938    Star,
939    Sharp,
940    Plus,
941    Eq,
942    Question,
943    Comma,
944    Semicolon,
945    KwAs,
946    KwDyn,
947    KwUse,
948    KwFor,
949    KwMod,
950    KwCrate,
951    KwType,
952    KwTrait,
953    KwFn,
954    KwMut,
955    KwConst,
956    KwExtern,
957    KwImpl,
958    KwImport,
959    Ident(&'a str),
960    Str(&'a str),
961    Number(usize),
962}
963
964impl<'a> Token<'a> {
965    fn ident_or_kw(ident: &'a str) -> Self {
966        match ident {
967            "as" => Token::KwAs,
968            "dyn" => Token::KwDyn,
969            "mod" => Token::KwMod,
970            "type" => Token::KwType,
971            "trait" => Token::KwTrait,
972            "crate" => Token::KwCrate,
973            "fn" => Token::KwFn,
974            "mut" => Token::KwMut,
975            "const" => Token::KwConst,
976            "use" => Token::KwUse,
977            "for" => Token::KwFor,
978            "extern" => Token::KwExtern,
979            "impl" => Token::KwImpl,
980            "import" => Token::KwImport,
981            x => Token::Ident(x),
982        }
983    }
984}
985
986impl Display for Token<'_> {
987    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
988        match self {
989            Token::Arrow => write!(f, "->"),
990            Token::AngleOpen => write!(f, "<"),
991            Token::AngleClose => write!(f, ">"),
992            Token::BracketOpen => write!(f, "["),
993            Token::BracketClose => write!(f, "]"),
994            Token::ParenOpen => write!(f, "("),
995            Token::ParenClose => write!(f, ")"),
996            Token::BraceOpen => write!(f, "{{"),
997            Token::BraceClose => write!(f, "}}"),
998            Token::Colon => write!(f, ":"),
999            Token::ColonColon => write!(f, "::"),
1000            Token::And => write!(f, "&"),
1001            Token::Star => write!(f, "*"),
1002            Token::Sharp => write!(f, "#"),
1003            Token::Plus => write!(f, "+"),
1004            Token::Eq => write!(f, "="),
1005            Token::Question => write!(f, "?"),
1006            Token::Comma => write!(f, ","),
1007            Token::Semicolon => write!(f, ";"),
1008            Token::KwAs => write!(f, "as"),
1009            Token::KwDyn => write!(f, "dyn"),
1010            Token::KwUse => write!(f, "use"),
1011            Token::KwFor => write!(f, "for"),
1012            Token::KwMod => write!(f, "mod"),
1013            Token::KwCrate => write!(f, "crate"),
1014            Token::KwType => write!(f, "type"),
1015            Token::KwTrait => write!(f, "trait"),
1016            Token::KwFn => write!(f, "fn"),
1017            Token::KwMut => write!(f, "mut"),
1018            Token::KwConst => write!(f, "const"),
1019            Token::KwExtern => write!(f, "extern"),
1020            Token::KwImpl => write!(f, "impl"),
1021            Token::KwImport => write!(f, "import"),
1022            Token::Ident(i) => write!(f, "{i}"),
1023            Token::Number(n) => write!(f, "{n}"),
1024            Token::Str(s) => write!(f, r#""{s}""#),
1025        }
1026    }
1027}
1028
1029fn lexer<'src>()
1030-> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, extra::Err<Rich<'src, char, Span>>> {
1031    let token = choice((
1032        choice([
1033            just("->").to(Token::Arrow),
1034            just("<").to(Token::AngleOpen),
1035            just(">").to(Token::AngleClose),
1036            just("[").to(Token::BracketOpen),
1037            just("]").to(Token::BracketClose),
1038            just("(").to(Token::ParenOpen),
1039            just(")").to(Token::ParenClose),
1040            just("{").to(Token::BraceOpen),
1041            just("}").to(Token::BraceClose),
1042            just("::").to(Token::ColonColon),
1043            just(":").to(Token::Colon),
1044            just("&").to(Token::And),
1045            just("*").to(Token::Star),
1046            just("#").to(Token::Sharp),
1047            just("+").to(Token::Plus),
1048            just("=").to(Token::Eq),
1049            just("?").to(Token::Question),
1050            just(",").to(Token::Comma),
1051            just(";").to(Token::Semicolon),
1052        ]),
1053        text::ident().map(Token::ident_or_kw),
1054        text::int(10).map(|x: &str| Token::Number(x.parse().unwrap())),
1055        just('"')
1056            .ignore_then(none_of('"').repeated().to_slice().map(Token::Str))
1057            .then_ignore(just('"')),
1058    ));
1059
1060    let comment = just("//")
1061        .then(any().and_is(just('\n').not()).repeated())
1062        .padded();
1063
1064    token
1065        .map_with(|tok, extra| (tok, extra.span()))
1066        .padded_by(comment.repeated())
1067        .padded()
1068        .repeated()
1069        .collect()
1070}
1071
1072fn alias<'a>()
1073-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1074    just(Token::KwUse)
1075        .ignore_then(path())
1076        .then_ignore(just(Token::KwAs))
1077        .then(select! {
1078            Token::Ident(c) => c,
1079        })
1080        .then_ignore(just(Token::Semicolon))
1081        .map_with(|(path, name), extra| {
1082            ParsedItem::Alias(ParsedAlias {
1083                name,
1084                path,
1085                span: extra.span(),
1086            })
1087        })
1088        .boxed()
1089}
1090
1091fn file_parser<'a>()
1092-> impl Parser<'a, ParserInput<'a>, ParsedZngFile<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone
1093{
1094    item().repeated().collect::<Vec<_>>().map(ParsedZngFile)
1095}
1096
1097fn rust_type<'a>()
1098-> Boxed<'a, 'a, ParserInput<'a>, ParsedRustType<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> {
1099    let as_scalar = |s: &str, head: char| -> Option<u32> {
1100        let s = s.strip_prefix(head)?;
1101        s.parse().ok()
1102    };
1103
1104    let scalar = select! {
1105        Token::Ident("bool") => PrimitiveRustType::Bool,
1106        Token::Ident("str") => PrimitiveRustType::Str,
1107        Token::Ident("ZngurCppOpaqueOwnedObject") => PrimitiveRustType::ZngurCppOpaqueOwnedObject,
1108        Token::Ident("usize") => PrimitiveRustType::Usize,
1109        Token::Ident(c) if as_scalar(c, 'u').is_some() => PrimitiveRustType::Uint(as_scalar(c, 'u').unwrap()),
1110        Token::Ident(c) if as_scalar(c, 'i').is_some() => PrimitiveRustType::Int(as_scalar(c, 'i').unwrap()),
1111        Token::Ident(c) if as_scalar(c, 'f').is_some() => PrimitiveRustType::Float(as_scalar(c, 'f').unwrap()),
1112    }.map(ParsedRustType::Primitive);
1113
1114    recursive(|parser| {
1115        let parser = parser.boxed();
1116        let pg = rust_path_and_generics(parser.clone());
1117        let adt = pg.clone().map(ParsedRustType::Adt);
1118
1119        let dyn_trait = just(Token::KwDyn)
1120            .ignore_then(rust_trait(parser.clone()))
1121            .then(
1122                just(Token::Plus)
1123                    .ignore_then(select! {
1124                        Token::Ident(c) => c,
1125                    })
1126                    .repeated()
1127                    .collect::<Vec<_>>(),
1128            )
1129            .map(|(x, y)| ParsedRustType::Dyn(x, y));
1130        let boxed = just(Token::Ident("Box"))
1131            .then(rust_generics(parser.clone()))
1132            .map(|(_, x)| {
1133                assert_eq!(x.len(), 1);
1134                ParsedRustType::Boxed(Box::new(x.into_iter().next().unwrap().right().unwrap()))
1135            });
1136        let unit = just(Token::ParenOpen)
1137            .then(just(Token::ParenClose))
1138            .map(|_| ParsedRustType::Tuple(vec![]));
1139        let tuple = parser
1140            .clone()
1141            .separated_by(just(Token::Comma))
1142            .allow_trailing()
1143            .collect::<Vec<_>>()
1144            .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1145            .map(|xs| ParsedRustType::Tuple(xs));
1146        let slice = parser
1147            .clone()
1148            .map(|x| ParsedRustType::Slice(Box::new(x)))
1149            .delimited_by(just(Token::BracketOpen), just(Token::BracketClose));
1150        let reference = just(Token::And)
1151            .ignore_then(
1152                just(Token::KwMut)
1153                    .to(Mutability::Mut)
1154                    .or(empty().to(Mutability::Not)),
1155            )
1156            .then(parser.clone())
1157            .map(|(m, x)| ParsedRustType::Ref(m, Box::new(x)));
1158        let raw_ptr = just(Token::Star)
1159            .ignore_then(
1160                just(Token::KwMut)
1161                    .to(Mutability::Mut)
1162                    .or(just(Token::KwConst).to(Mutability::Not)),
1163            )
1164            .then(parser)
1165            .map(|(m, x)| ParsedRustType::Raw(m, Box::new(x)));
1166        choice((
1167            scalar, boxed, unit, tuple, slice, adt, reference, raw_ptr, dyn_trait,
1168        ))
1169    })
1170    .boxed()
1171}
1172
1173fn rust_generics<'a>(
1174    rust_type: Boxed<
1175        'a,
1176        'a,
1177        ParserInput<'a>,
1178        ParsedRustType<'a>,
1179        extra::Err<Rich<'a, Token<'a>, Span>>,
1180    >,
1181) -> impl Parser<
1182    'a,
1183    ParserInput<'a>,
1184    Vec<Either<(&'a str, ParsedRustType<'a>), ParsedRustType<'a>>>,
1185    extra::Err<Rich<'a, Token<'a>, Span>>,
1186> + Clone {
1187    let named_generic = select! {
1188        Token::Ident(c) => c,
1189    }
1190    .then_ignore(just(Token::Eq))
1191    .then(rust_type.clone())
1192    .map(Either::Left);
1193    just(Token::ColonColon).repeated().at_most(1).ignore_then(
1194        named_generic
1195            .or(rust_type.clone().map(Either::Right))
1196            .separated_by(just(Token::Comma))
1197            .allow_trailing()
1198            .collect::<Vec<_>>()
1199            .delimited_by(just(Token::AngleOpen), just(Token::AngleClose)),
1200    )
1201}
1202
1203fn rust_path_and_generics<'a>(
1204    rust_type: Boxed<
1205        'a,
1206        'a,
1207        ParserInput<'a>,
1208        ParsedRustType<'a>,
1209        extra::Err<Rich<'a, Token<'a>, Span>>,
1210    >,
1211) -> impl Parser<
1212    'a,
1213    ParserInput<'a>,
1214    ParsedRustPathAndGenerics<'a>,
1215    extra::Err<Rich<'a, Token<'a>, Span>>,
1216> + Clone {
1217    let generics = rust_generics(rust_type.clone());
1218    path()
1219        .then(generics.clone().repeated().at_most(1).collect::<Vec<_>>())
1220        .map(|x| {
1221            let generics = x.1.into_iter().next().unwrap_or_default();
1222            let (named_generics, generics) = generics.into_iter().partition_map(|x| x);
1223            ParsedRustPathAndGenerics {
1224                path: x.0,
1225                generics,
1226                named_generics,
1227            }
1228        })
1229}
1230
1231fn fn_args<'a>(
1232    rust_type: Boxed<
1233        'a,
1234        'a,
1235        ParserInput<'a>,
1236        ParsedRustType<'a>,
1237        extra::Err<Rich<'a, Token<'a>, Span>>,
1238    >,
1239) -> impl Parser<
1240    'a,
1241    ParserInput<'a>,
1242    (Vec<ParsedRustType<'a>>, ParsedRustType<'a>),
1243    extra::Err<Rich<'a, Token<'a>, Span>>,
1244> + Clone {
1245    rust_type
1246        .clone()
1247        .separated_by(just(Token::Comma))
1248        .allow_trailing()
1249        .collect::<Vec<_>>()
1250        .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1251        .then(
1252            just(Token::Arrow)
1253                .ignore_then(rust_type)
1254                .or(empty().to(ParsedRustType::Tuple(vec![]))),
1255        )
1256        .boxed()
1257}
1258
1259fn spanned<'a, T>(
1260    parser: impl Parser<'a, ParserInput<'a>, T, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone,
1261) -> impl Parser<'a, ParserInput<'a>, Spanned<T>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1262    parser.map_with(|inner, extra| Spanned {
1263        inner,
1264        span: extra.span(),
1265    })
1266}
1267
1268fn rust_trait<'a>(
1269    rust_type: Boxed<
1270        'a,
1271        'a,
1272        ParserInput<'a>,
1273        ParsedRustType<'a>,
1274        extra::Err<Rich<'a, Token<'a>, Span>>,
1275    >,
1276) -> impl Parser<'a, ParserInput<'a>, ParsedRustTrait<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone
1277{
1278    let fn_trait = select! {
1279        Token::Ident(c) => c,
1280    }
1281    .then(fn_args(rust_type.clone()))
1282    .map(|x| ParsedRustTrait::Fn {
1283        name: x.0,
1284        inputs: x.1.0,
1285        output: Box::new(x.1.1),
1286    });
1287
1288    let rust_trait = fn_trait.or(rust_path_and_generics(rust_type).map(ParsedRustTrait::Normal));
1289    rust_trait
1290}
1291
1292fn method<'a>()
1293-> impl Parser<'a, ParserInput<'a>, ParsedMethod<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone
1294{
1295    just(Token::KwFn)
1296        .ignore_then(select! {
1297            Token::Ident(c) => c,
1298        })
1299        .then(
1300            rust_type()
1301                .separated_by(just(Token::Comma))
1302                .collect::<Vec<_>>()
1303                .delimited_by(just(Token::AngleOpen), just(Token::AngleClose))
1304                .or(empty().to(vec![])),
1305        )
1306        .then(fn_args(rust_type()))
1307        .map(|((name, generics), args)| {
1308            let is_self = |c: &ParsedRustType<'_>| {
1309                if let ParsedRustType::Adt(c) = c {
1310                    c.path.start == ParsedPathStart::Relative
1311                        && &c.path.segments == &["self"]
1312                        && c.generics.is_empty()
1313                } else {
1314                    false
1315                }
1316            };
1317            let (inputs, receiver) = match args.0.get(0) {
1318                Some(x) if is_self(&x) => (args.0[1..].to_vec(), ZngurMethodReceiver::Move),
1319                Some(ParsedRustType::Ref(m, x)) if is_self(&x) => {
1320                    (args.0[1..].to_vec(), ZngurMethodReceiver::Ref(*m))
1321                }
1322                _ => (args.0, ZngurMethodReceiver::Static),
1323            };
1324            ParsedMethod {
1325                name,
1326                receiver,
1327                generics,
1328                inputs,
1329                output: args.1,
1330            }
1331        })
1332}
1333
1334fn type_item<'a>()
1335-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1336    fn inner_item<'a>()
1337    -> impl Parser<'a, ParserInput<'a>, ParsedTypeItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>>
1338    + Clone {
1339        let property_item = (spanned(select! {
1340            Token::Ident(c) => c,
1341        }))
1342        .then_ignore(just(Token::Eq))
1343        .then(select! {
1344            Token::Number(c) => c,
1345        });
1346        let layout = just([Token::Sharp, Token::Ident("layout")])
1347            .ignore_then(
1348                property_item
1349                    .separated_by(just(Token::Comma))
1350                    .collect::<Vec<_>>()
1351                    .delimited_by(just(Token::ParenOpen), just(Token::ParenClose)),
1352            )
1353            .map(ParsedLayoutPolicy::StackAllocated)
1354            .or(just([Token::Sharp, Token::Ident("only_by_ref")]).to(ParsedLayoutPolicy::OnlyByRef))
1355            .or(just([Token::Sharp, Token::Ident("heap_allocated")])
1356                .to(ParsedLayoutPolicy::HeapAllocated))
1357            .map_with(|x, extra| ParsedTypeItem::Layout(extra.span(), x))
1358            .boxed();
1359        let trait_item = select! {
1360            Token::Ident("Debug") => ZngurWellknownTrait::Debug,
1361            Token::Ident("Copy") => ZngurWellknownTrait::Copy,
1362        }
1363        .or(just(Token::Question)
1364            .then(just(Token::Ident("Sized")))
1365            .to(ZngurWellknownTrait::Unsized));
1366        let traits = just(Token::Ident("wellknown_traits"))
1367            .ignore_then(
1368                spanned(trait_item)
1369                    .separated_by(just(Token::Comma))
1370                    .collect::<Vec<_>>()
1371                    .delimited_by(just(Token::ParenOpen), just(Token::ParenClose)),
1372            )
1373            .map(ParsedTypeItem::Traits)
1374            .boxed();
1375        let constructor_args = rust_type()
1376            .separated_by(just(Token::Comma))
1377            .collect::<Vec<_>>()
1378            .delimited_by(just(Token::ParenOpen), just(Token::ParenClose))
1379            .map(ParsedConstructorArgs::Tuple)
1380            .or((select! {
1381                Token::Ident(c) => c,
1382            })
1383            .boxed()
1384            .then_ignore(just(Token::Colon))
1385            .then(rust_type())
1386            .separated_by(just(Token::Comma))
1387            .collect::<Vec<_>>()
1388            .delimited_by(just(Token::BraceOpen), just(Token::BraceClose))
1389            .map(ParsedConstructorArgs::Named))
1390            .or(empty().to(ParsedConstructorArgs::Unit))
1391            .boxed();
1392        let constructor = just(Token::Ident("constructor")).ignore_then(
1393            (select! {
1394                Token::Ident(c) => Some(c),
1395            })
1396            .or(empty().to(None))
1397            .then(constructor_args)
1398            .map(|(name, args)| ParsedTypeItem::Constructor { name, args }),
1399        );
1400        let field = just(Token::Ident("field")).ignore_then(
1401            (select! {
1402                Token::Ident(c) => c.to_owned(),
1403                Token::Number(c) => c.to_string(),
1404            })
1405            .then(
1406                just(Token::Ident("offset"))
1407                    .then(just(Token::Eq))
1408                    .ignore_then(select! {
1409                        Token::Number(c) => c,
1410                    })
1411                    .then(
1412                        just(Token::Comma)
1413                            .then(just(Token::KwType))
1414                            .then(just(Token::Eq))
1415                            .ignore_then(rust_type()),
1416                    )
1417                    .delimited_by(just(Token::ParenOpen), just(Token::ParenClose)),
1418            )
1419            .map(|(name, (offset, ty))| ParsedTypeItem::Field { name, ty, offset }),
1420        );
1421        let cpp_value = just(Token::Sharp)
1422            .then(just(Token::Ident("cpp_value")))
1423            .ignore_then(select! {
1424                Token::Str(c) => c,
1425            })
1426            .then(select! {
1427                Token::Str(c) => c,
1428            })
1429            .map(|x| ParsedTypeItem::CppValue {
1430                field: x.0,
1431                cpp_type: x.1,
1432            });
1433        let cpp_ref = just(Token::Sharp)
1434            .then(just(Token::Ident("cpp_ref")))
1435            .ignore_then(select! {
1436                Token::Str(c) => c,
1437            })
1438            .map(|x| ParsedTypeItem::CppRef { cpp_type: x });
1439        choice((
1440            layout,
1441            traits,
1442            constructor,
1443            field,
1444            cpp_value,
1445            cpp_ref,
1446            method()
1447                .then(
1448                    just(Token::KwUse)
1449                        .ignore_then(path())
1450                        .map(Some)
1451                        .or(empty().to(None)),
1452                )
1453                .then(
1454                    just(Token::Ident("deref"))
1455                        .ignore_then(rust_type())
1456                        .map(Some)
1457                        .or(empty().to(None)),
1458                )
1459                .map(|((data, use_path), deref)| ParsedTypeItem::Method {
1460                    deref,
1461                    use_path,
1462                    data,
1463                }),
1464        ))
1465        .then_ignore(just(Token::Semicolon))
1466    }
1467    just(Token::KwType)
1468        .ignore_then(spanned(rust_type()))
1469        .then(
1470            spanned(inner_item())
1471                .repeated()
1472                .collect::<Vec<_>>()
1473                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)),
1474        )
1475        .map(|(ty, items)| ParsedItem::Type { ty, items })
1476        .boxed()
1477}
1478
1479fn trait_item<'a>()
1480-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1481    just(Token::KwTrait)
1482        .ignore_then(spanned(rust_trait(rust_type())))
1483        .then(
1484            method()
1485                .then_ignore(just(Token::Semicolon))
1486                .repeated()
1487                .collect::<Vec<_>>()
1488                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)),
1489        )
1490        .map(|(tr, methods)| ParsedItem::Trait { tr, methods })
1491        .boxed()
1492}
1493
1494fn fn_item<'a>()
1495-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1496    spanned(method())
1497        .then_ignore(just(Token::Semicolon))
1498        .map(ParsedItem::Fn)
1499}
1500
1501fn additional_include_item<'a>()
1502-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1503    just(Token::Sharp)
1504        .ignore_then(
1505            just(Token::Ident("cpp_additional_includes"))
1506                .ignore_then(select! {
1507                    Token::Str(c) => ParsedItem::CppAdditionalInclude(c),
1508                })
1509                .or(just(Token::Ident("convert_panic_to_exception"))
1510                    .map_with(|_, extra| ParsedItem::ConvertPanicToException(extra.span()))),
1511        )
1512        .boxed()
1513}
1514
1515fn extern_cpp_item<'a>()
1516-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1517    let function = spanned(method())
1518        .then_ignore(just(Token::Semicolon))
1519        .map(ParsedExternCppItem::Function);
1520    let impl_block = just(Token::KwImpl)
1521        .ignore_then(
1522            rust_trait(rust_type())
1523                .then_ignore(just(Token::KwFor))
1524                .map(Some)
1525                .or(empty().to(None))
1526                .then(spanned(rust_type())),
1527        )
1528        .then(
1529            method()
1530                .then_ignore(just(Token::Semicolon))
1531                .repeated()
1532                .collect::<Vec<_>>()
1533                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)),
1534        )
1535        .map(|((tr, ty), methods)| ParsedExternCppItem::Impl { tr, ty, methods });
1536    just(Token::KwExtern)
1537        .then(just(Token::Str("C++")))
1538        .ignore_then(
1539            function
1540                .or(impl_block)
1541                .repeated()
1542                .collect::<Vec<_>>()
1543                .delimited_by(just(Token::BraceOpen), just(Token::BraceClose))
1544                .boxed(),
1545        )
1546        .map(ParsedItem::ExternCpp)
1547        .boxed()
1548}
1549
1550fn item<'a>()
1551-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1552    recursive(|item| {
1553        choice((
1554            just(Token::KwMod)
1555                .ignore_then(path())
1556                .then(
1557                    item.repeated()
1558                        .collect::<Vec<_>>()
1559                        .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)),
1560                )
1561                .map(|(path, items)| ParsedItem::Mod { path, items }),
1562            type_item(),
1563            trait_item(),
1564            extern_cpp_item(),
1565            fn_item(),
1566            additional_include_item(),
1567            import_item(),
1568            alias(),
1569        ))
1570    })
1571    .boxed()
1572}
1573
1574fn import_item<'a>()
1575-> impl Parser<'a, ParserInput<'a>, ParsedItem<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1576    just(Token::KwImport)
1577        .ignore_then(select! {
1578            Token::Str(path) => path,
1579        })
1580        .then_ignore(just(Token::Semicolon))
1581        .map_with(|path, extra| {
1582            ParsedItem::Import(ParsedImportPath {
1583                path: std::path::PathBuf::from(path),
1584                span: extra.span(),
1585            })
1586        })
1587        .boxed()
1588}
1589
1590fn path<'a>()
1591-> impl Parser<'a, ParserInput<'a>, ParsedPath<'a>, extra::Err<Rich<'a, Token<'a>, Span>>> + Clone {
1592    let start = choice((
1593        just(Token::ColonColon).to(ParsedPathStart::Absolute),
1594        just(Token::KwCrate)
1595            .then(just(Token::ColonColon))
1596            .to(ParsedPathStart::Crate),
1597        empty().to(ParsedPathStart::Relative),
1598    ));
1599    start
1600        .then(
1601            (select! {
1602                Token::Ident(c) => c,
1603            })
1604            .separated_by(just(Token::ColonColon))
1605            .at_least(1)
1606            .collect::<Vec<_>>(),
1607        )
1608        .or(just(Token::KwCrate).to((ParsedPathStart::Crate, vec![])))
1609        .map_with(|(start, segments), extra| ParsedPath {
1610            start,
1611            segments,
1612            span: extra.span(),
1613        })
1614        .boxed()
1615}