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#[derive(Debug)]
22pub struct ParseResult {
23 pub spec: ZngurSpec,
25 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
73trait 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 fn new_root(aliases: Vec<ParsedAlias<'a>>) -> Scope<'a> {
112 Scope {
113 aliases,
114 base: Vec::new(),
115 }
116 }
117
118 fn resolve_path(&self, path: ParsedPath<'a>) -> Vec<String> {
120 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 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 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(); 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!() }
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!() }
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 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 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
948pub trait ImportResolver {
950 fn resolve_import(
951 &self,
952 cwd: &std::path::Path,
953 relpath: &std::path::Path,
954 ) -> Result<String, String>;
955}
956
957struct 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 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 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 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 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 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() .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}