Skip to main content

zngur_parser/
lib.rs

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