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