1use std::{
36 cmp::Ordering,
37 fmt::{Debug, Display, Formatter},
38 hash::{Hash, Hasher},
39 ops::{Bound, Range, RangeFrom, RangeInclusive},
40};
41
42use compact_str::CompactString;
43use lady_deirdre::{
44 arena::{Entry, Id, Identifiable},
45 lexis::{
46 Column,
47 Line,
48 Position,
49 PositionSpan,
50 SiteRef,
51 SiteRefSpan,
52 SiteSpan,
53 SourceCode,
54 ToSpan,
55 TokenRef,
56 },
57 syntax::PolyRef,
58};
59
60use crate::runtime::{Ident, PackageMeta, ScriptIdent};
61
62#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub enum Origin {
81 Rust(&'static RustOrigin),
83
84 Script(ScriptOrigin),
86}
87
88impl Default for Origin {
89 #[inline(always)]
90 fn default() -> Self {
91 Self::nil()
92 }
93}
94
95impl Debug for Origin {
96 #[inline(always)]
97 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
98 match self {
99 Self::Rust(origin) => Debug::fmt(origin, formatter),
100 Self::Script(origin) => Debug::fmt(origin, formatter),
101 }
102 }
103}
104
105impl From<&'static RustOrigin> for Origin {
106 #[inline(always)]
107 fn from(value: &'static RustOrigin) -> Self {
108 Self::Rust(value)
109 }
110}
111
112impl From<ScriptOrigin> for Origin {
113 #[inline(always)]
114 fn from(value: ScriptOrigin) -> Self {
115 Self::Script(value)
116 }
117}
118
119impl From<SiteRefSpan> for Origin {
120 #[inline(always)]
121 fn from(value: SiteRefSpan) -> Self {
122 Self::Script(ScriptOrigin::from(value))
123 }
124}
125
126impl<'a> From<&'a SiteRefSpan> for Origin {
127 #[inline(always)]
128 fn from(value: &'a SiteRefSpan) -> Self {
129 Self::Script(ScriptOrigin::from(value))
130 }
131}
132
133impl From<SiteRef> for Origin {
134 #[inline(always)]
135 fn from(value: SiteRef) -> Self {
136 Self::Script(ScriptOrigin::from(value))
137 }
138}
139
140impl<'a> From<&'a SiteRef> for Origin {
141 #[inline(always)]
142 fn from(value: &'a SiteRef) -> Self {
143 Self::Script(ScriptOrigin::from(value))
144 }
145}
146
147impl From<TokenRef> for Origin {
148 #[inline(always)]
149 fn from(value: TokenRef) -> Self {
150 Self::Script(ScriptOrigin::from(value))
151 }
152}
153
154impl<'a> From<&'a TokenRef> for Origin {
155 fn from(value: &'a TokenRef) -> Self {
156 Self::Script(ScriptOrigin::from(value))
157 }
158}
159
160impl From<Range<TokenRef>> for Origin {
161 #[inline(always)]
162 fn from(value: Range<TokenRef>) -> Self {
163 Self::Script(ScriptOrigin::from(value))
164 }
165}
166
167impl<'a> From<Range<&'a TokenRef>> for Origin {
168 #[inline(always)]
169 fn from(value: Range<&'a TokenRef>) -> Self {
170 Self::Script(ScriptOrigin::from(value))
171 }
172}
173
174impl From<RangeInclusive<TokenRef>> for Origin {
175 #[inline(always)]
176 fn from(value: RangeInclusive<TokenRef>) -> Self {
177 Self::Script(ScriptOrigin::from(value))
178 }
179}
180
181impl<'a> From<RangeInclusive<&'a TokenRef>> for Origin {
182 #[inline(always)]
183 fn from(value: RangeInclusive<&'a TokenRef>) -> Self {
184 Self::Script(ScriptOrigin::from(value))
185 }
186}
187
188impl From<RangeFrom<TokenRef>> for Origin {
189 #[inline(always)]
190 fn from(value: RangeFrom<TokenRef>) -> Self {
191 Self::Script(ScriptOrigin::from(value))
192 }
193}
194
195impl<'a> From<RangeFrom<&'a TokenRef>> for Origin {
196 #[inline(always)]
197 fn from(value: RangeFrom<&'a TokenRef>) -> Self {
198 Self::Script(ScriptOrigin::from(value))
199 }
200}
201
202impl From<RangeFrom<SiteRef>> for Origin {
203 #[inline(always)]
204 fn from(value: RangeFrom<SiteRef>) -> Self {
205 Self::Script(ScriptOrigin::from(value))
206 }
207}
208
209impl<'a> From<RangeFrom<&'a SiteRef>> for Origin {
210 #[inline(always)]
211 fn from(value: RangeFrom<&'a SiteRef>) -> Self {
212 Self::Script(ScriptOrigin::from(value))
213 }
214}
215
216impl Origin {
217 #[inline(always)]
225 pub fn nil() -> Self {
226 Self::Rust(&RustOrigin::nil())
227 }
228
229 #[inline(always)]
231 pub fn is_nil(&self) -> bool {
232 match self {
233 Self::Rust(origin) => origin.is_nil(),
234 Self::Script(origin) => origin.is_nil(),
235 }
236 }
237
238 #[inline(always)]
244 pub fn package(&self) -> Option<&'static PackageMeta> {
245 match self {
246 Self::Rust(origin) => origin.package(),
247 Self::Script(origin) => origin.package(),
248 }
249 }
250
251 #[inline(always)]
252 pub(crate) fn into_ident(self, string: impl Into<CompactString>) -> Ident {
253 match self {
254 Self::Rust(..) => Ident::Script(ScriptIdent::from_string(TokenRef::nil(), string)),
255
256 Self::Script(origin) => {
257 Ident::Script(ScriptIdent::from_string(origin.into_token_ref(), string))
258 }
259 }
260 }
261}
262
263static NIL_RUST_ORIGIN: RustOrigin = RustOrigin {
264 package: None,
265 code: None,
266};
267
268#[derive(Clone, Copy, PartialOrd, Ord, Hash)]
279pub struct RustOrigin {
280 pub package: Option<(&'static str, &'static str)>,
282
283 pub code: Option<RustCode>,
285}
286
287impl Default for RustOrigin {
288 #[inline(always)]
289 fn default() -> Self {
290 NIL_RUST_ORIGIN
291 }
292}
293
294impl PartialEq for RustOrigin {
295 #[inline]
296 fn eq(&self, other: &Self) -> bool {
297 match (&self.package, &other.package) {
298 (Some(this), Some(other)) => {
299 if this.ne(other) {
300 return false;
301 }
302 }
303
304 _ => return false,
305 }
306
307 if let (Some(this), Some(other)) = (&self.code, &other.code) {
308 if this.ne(other) {
309 return false;
310 }
311 }
312
313 true
314 }
315}
316
317impl Eq for RustOrigin {}
318
319impl Debug for RustOrigin {
320 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
321 if self.package.is_none() && self.code.is_none() {
322 return formatter.write_str("RustOrigin(invalid)");
323 }
324
325 let mut debug_struct = formatter.debug_struct("RustOrigin");
326
327 if let Some((name, version)) = &self.package {
328 debug_struct.field("package", &format_args!("{name}@{version}"));
329 }
330
331 if let Some(code) = &self.code {
332 debug_struct.field("code", &code);
333 }
334
335 debug_struct.finish()
336 }
337}
338
339impl Display for RustOrigin {
340 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
341 if let Some(code) = &self.code {
342 return Display::fmt(code, formatter);
343 }
344
345 if let Some((name, _)) = self.package {
346 return formatter.write_str(name);
347 }
348
349 formatter.write_str("[?]")
350 }
351}
352
353impl RustOrigin {
354 #[inline(always)]
357 pub fn nil() -> &'static Self {
358 &NIL_RUST_ORIGIN
359 }
360
361 pub fn is_nil(&self) -> bool {
363 self == &NIL_RUST_ORIGIN
364 }
365
366 #[inline(always)]
372 pub fn package(&self) -> Option<&'static PackageMeta> {
373 if let Some((name, version)) = self.package {
374 return PackageMeta::of(name, &format!("={}", version));
375 }
376
377 None
378 }
379
380 #[inline(never)]
385 pub fn blame<T>(&self, message: &str) -> T {
386 if let Some(code) = self.code {
387 (code.blame_fn)(message);
388 }
389
390 match self.package {
391 Some((name, _)) => panic!("{}: {}", name, message),
392 None => panic!("{}", message),
393 }
394 }
395}
396
397#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
422pub struct ScriptOrigin {
423 id: Id,
424 start: Option<Entry>,
425 end: Bound<Entry>,
426}
427
428impl Default for ScriptOrigin {
429 #[inline(always)]
430 fn default() -> Self {
431 Self::nil()
432 }
433}
434
435impl PartialOrd for ScriptOrigin {
436 #[inline(always)]
437 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
438 Some(self.cmp(other))
439 }
440}
441
442impl Ord for ScriptOrigin {
443 #[inline(always)]
444 fn cmp(&self, other: &Self) -> Ordering {
445 match self.id.cmp(&other.id) {
446 Ordering::Equal => match self.start.cmp(&other.start) {
447 Ordering::Equal => match (&self.end, &other.end) {
448 (Bound::Included(this), Bound::Included(other)) => this.cmp(other),
449 (Bound::Included(..), Bound::Excluded(..)) => Ordering::Less,
450 (Bound::Included(..), Bound::Unbounded) => Ordering::Less,
451
452 (Bound::Excluded(..), Bound::Included(..)) => Ordering::Greater,
453 (Bound::Excluded(this), Bound::Excluded(other)) => this.cmp(other),
454 (Bound::Excluded(..), Bound::Unbounded) => Ordering::Less,
455
456 (Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
457 (Bound::Unbounded, _) => Ordering::Greater,
458 },
459
460 other => other,
461 },
462
463 other => other,
464 }
465 }
466}
467
468impl Identifiable for ScriptOrigin {
469 #[inline(always)]
470 fn id(&self) -> Id {
471 self.id
472 }
473}
474
475impl From<SiteRefSpan> for ScriptOrigin {
476 #[inline(always)]
477 fn from(value: SiteRefSpan) -> Self {
478 Self::from(&value)
479 }
480}
481
482impl<'a> From<&'a SiteRefSpan> for ScriptOrigin {
483 #[inline(always)]
484 fn from(value: &'a SiteRefSpan) -> Self {
485 let id = value.start.id();
486
487 if id.is_nil() || id != value.end.id() {
488 return Self::default();
489 }
490
491 Self {
492 id,
493
494 start: {
495 let bound = value.start.token_ref();
496
497 match bound.is_nil() {
498 true => None,
499 false => Some(bound.entry),
500 }
501 },
502
503 end: {
504 let bound = value.end.token_ref();
505
506 match bound.is_nil() {
507 true => Bound::Unbounded,
508 false => Bound::Excluded(bound.entry),
509 }
510 },
511 }
512 }
513}
514
515impl From<SiteRef> for ScriptOrigin {
516 #[inline(always)]
517 fn from(value: SiteRef) -> Self {
518 Self::from(&value)
519 }
520}
521
522impl<'a> From<&'a SiteRef> for ScriptOrigin {
523 #[inline(always)]
524 fn from(value: &'a SiteRef) -> Self {
525 let id = value.id();
526
527 if id.is_nil() {
528 return Self::default();
529 }
530
531 let bound = value.token_ref();
532
533 match bound.id.is_nil() {
534 true => Self {
535 id,
536 start: None,
537 end: Bound::Unbounded,
538 },
539
540 false => Self {
541 id,
542 start: Some(bound.entry),
543 end: Bound::Excluded(bound.entry),
544 },
545 }
546 }
547}
548
549impl From<TokenRef> for ScriptOrigin {
550 #[inline(always)]
551 fn from(value: TokenRef) -> Self {
552 Self::from(&value)
553 }
554}
555
556impl<'a> From<&'a TokenRef> for ScriptOrigin {
557 #[inline(always)]
558 fn from(value: &'a TokenRef) -> Self {
559 Self {
560 id: value.id,
561 start: Some(value.entry),
562 end: Bound::Included(value.entry),
563 }
564 }
565}
566
567impl From<Range<TokenRef>> for ScriptOrigin {
568 #[inline(always)]
569 fn from(value: Range<TokenRef>) -> Self {
570 Self::from(&value.start..&value.end)
571 }
572}
573
574impl<'a> From<Range<&'a TokenRef>> for ScriptOrigin {
575 #[inline(always)]
576 fn from(value: Range<&'a TokenRef>) -> Self {
577 let id = value.start.id;
578
579 if id.is_nil() || id != value.end.id {
580 return Self::default();
581 }
582
583 Self {
584 id,
585 start: Some(value.start.entry),
586 end: Bound::Excluded(value.end.entry),
587 }
588 }
589}
590
591impl From<RangeInclusive<TokenRef>> for ScriptOrigin {
592 #[inline(always)]
593 fn from(value: RangeInclusive<TokenRef>) -> Self {
594 Self::from(value.start()..=value.end())
595 }
596}
597
598impl<'a> From<RangeInclusive<&'a TokenRef>> for ScriptOrigin {
599 #[inline(always)]
600 fn from(value: RangeInclusive<&'a TokenRef>) -> Self {
601 let id = value.start().id;
602
603 if id.is_nil() || id != value.end().id {
604 return Self::default();
605 }
606
607 Self {
608 id,
609 start: Some(value.start().entry),
610 end: Bound::Included(value.end().entry),
611 }
612 }
613}
614
615impl From<RangeFrom<TokenRef>> for ScriptOrigin {
616 #[inline(always)]
617 fn from(value: RangeFrom<TokenRef>) -> Self {
618 Self::from(&value.start..)
619 }
620}
621
622impl<'a> From<RangeFrom<&'a TokenRef>> for ScriptOrigin {
623 #[inline(always)]
624 fn from(value: RangeFrom<&'a TokenRef>) -> Self {
625 let id = value.start.id();
626
627 if id.is_nil() {
628 return Self::default();
629 }
630
631 Self {
632 id: value.start.id,
633 start: Some(value.start.entry),
634 end: Bound::Unbounded,
635 }
636 }
637}
638
639impl From<RangeFrom<SiteRef>> for ScriptOrigin {
640 #[inline(always)]
641 fn from(value: RangeFrom<SiteRef>) -> Self {
642 Self::from(&value.start..)
643 }
644}
645
646impl<'a> From<RangeFrom<&'a SiteRef>> for ScriptOrigin {
647 #[inline(always)]
648 fn from(value: RangeFrom<&'a SiteRef>) -> Self {
649 let id = value.start.id();
650
651 if id.is_nil() {
652 return Self::default();
653 }
654
655 let bound = value.start.token_ref();
656
657 match bound.id.is_nil() {
658 true => Self {
659 id,
660 start: None,
661 end: Bound::Unbounded,
662 },
663
664 false => Self {
665 id,
666 start: Some(bound.entry),
667 end: Bound::Unbounded,
668 },
669 }
670 }
671}
672
673unsafe impl ToSpan for ScriptOrigin {
675 #[inline(always)]
676 fn to_site_span(&self, code: &impl SourceCode) -> Option<SiteSpan> {
677 if self.id != code.id() {
678 return None;
679 }
680
681 let length = code.length();
682
683 let start = match &self.start {
684 Some(entry) => code.get_site(entry)?.min(length),
685 None => length,
686 };
687
688 let end = match &self.end {
689 Bound::Included(entry) => {
690 let chunk_length = code.get_length(entry)?;
691 let bound = (code.get_site(entry)? + chunk_length).min(length);
692
693 if bound < start {
694 return None;
695 }
696
697 bound
698 }
699
700 Bound::Excluded(entry) => {
701 let bound = code.get_site(entry)?;
702
703 if bound < start {
704 return None;
705 }
706
707 bound
708 }
709
710 Bound::Unbounded => length,
711 };
712
713 Some(start..end)
714 }
715
716 #[inline(always)]
717 fn is_valid_span(&self, code: &impl SourceCode) -> bool {
718 self.to_site_span(code).is_some()
719 }
720}
721
722impl ScriptOrigin {
723 #[inline(always)]
727 pub const fn nil() -> Self {
728 Self {
729 id: Id::nil(),
730 start: None,
731 end: Bound::Unbounded,
732 }
733 }
734
735 #[inline(always)]
736 pub(crate) fn invalid(id: Id) -> Self {
737 Self {
738 id,
739 start: Some(Entry::nil()),
740 end: Bound::Unbounded,
741 }
742 }
743
744 #[inline(always)]
745 pub(crate) fn eoi(id: Id) -> Self {
746 Self {
747 id,
748 start: None,
749 end: Bound::Unbounded,
750 }
751 }
752
753 #[inline(always)]
760 pub fn package(&self) -> Option<&'static PackageMeta> {
761 PackageMeta::by_id(self.id)
762 }
763
764 #[inline(always)]
766 pub const fn is_nil(&self) -> bool {
767 if self.id.is_nil() {
768 return true;
769 }
770
771 if let Some(entry) = &self.start {
772 if entry.is_nil() {
773 return true;
774 }
775 }
776
777 match &self.end {
778 Bound::Included(entry) | Bound::Excluded(entry) => {
779 if entry.is_nil() {
780 return true;
781 }
782 }
783 _ => (),
784 }
785
786 false
787 }
788
789 #[inline(always)]
790 pub(crate) fn union(&mut self, other: &Self) {
791 self.end = other.end;
792 }
793
794 #[inline(always)]
795 pub(crate) fn unbound(&mut self) {
796 self.end = Bound::Unbounded;
797 }
798
799 fn into_token_ref(self) -> TokenRef {
800 let Some(entry) = self.start else {
801 return TokenRef::nil();
802 };
803
804 TokenRef { id: self.id, entry }
805 }
806}
807
808#[derive(Clone, Copy, PartialOrd, Ord)]
811pub struct RustCode {
812 pub module: &'static str,
814
815 pub line: u32,
817
818 pub column: u32,
820
821 pub blame_fn: fn(&str),
824}
825
826impl Hash for RustCode {
827 #[inline]
828 fn hash<H: Hasher>(&self, state: &mut H) {
829 self.module.hash(state);
830 self.line.hash(state);
831 self.column.hash(state);
832 }
833}
834
835impl PartialEq for RustCode {
836 #[inline]
837 fn eq(&self, other: &Self) -> bool {
838 if self.module.ne(other.module) {
839 return false;
840 }
841
842 if self.line.ne(&other.line) {
843 return false;
844 }
845
846 if self.column.ne(&other.column) {
847 return false;
848 }
849
850 true
851 }
852}
853
854impl Eq for RustCode {}
855
856impl Debug for RustCode {
857 #[inline(always)]
858 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
859 formatter
860 .debug_struct("RustCode")
861 .field("module", &self.module)
862 .field("position", &format_args!("{}", self.position()))
863 .finish()
864 }
865}
866
867impl Display for RustCode {
868 #[inline(always)]
869 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
870 formatter.write_fmt(format_args!("{} [{}]", self.module, self.span_string()))
871 }
872}
873
874impl RustCode {
875 #[inline(never)]
877 pub fn blame(&self, message: &str) {
878 (self.blame_fn)(message);
879 panic!("{}: {}", self, message);
880 }
881
882 #[inline(always)]
885 pub fn position(&self) -> Position {
886 Position::new(self.line as Line, self.column as Column)
887 }
888
889 #[inline(always)]
892 pub fn span(&self) -> PositionSpan {
893 let bound = self.position();
894
895 bound..bound
896 }
897
898 pub fn span_string(&self) -> String {
901 format!("{}:{}", self.line, self.column)
902 }
903}