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