1use alloc::{borrow::Cow, collections::VecDeque, format};
2use core::fmt;
3
4use midenc_session::diagnostics::{miette, Diagnostic};
5use smallvec::{smallvec, SmallVec};
6
7use super::SymbolUseRef;
8use crate::{define_attr_type, interner, FunctionIdent, SymbolName};
9
10#[derive(Debug, thiserror::Error, Diagnostic)]
11pub enum InvalidSymbolPathError {
12 #[error("invalid symbol path: cannot be empty")]
13 Empty,
14 #[error("invalid symbol path: invalid format")]
15 #[diagnostic(help(
16 "The grammar for symbols is `<namespace>:<package>[/<export>]*[@<version>]"
17 ))]
18 InvalidFormat,
19 #[error("invalid symbol path: missing package")]
20 #[diagnostic(help(
21 "A fully-qualified symbol must namespace packages, i.e. `<namespace>:<package>`, but \
22 you've only provided one of these"
23 ))]
24 MissingPackage,
25 #[error("invalid symbol path: only fully-qualified symbols can be versioned")]
26 UnexpectedVersion,
27 #[error("invalid symbol path: unexpected character '{token}' at byte {pos}")]
28 UnexpectedToken { token: char, pos: usize },
29 #[error("invalid symbol path: no leaf component was provided")]
30 MissingLeaf,
31 #[error("invalid symbol path: unexpected components found after leaf")]
32 UnexpectedTrailingComponents,
33 #[error("invalid symbol path: only one root component is allowed, and it must come first")]
34 UnexpectedRootPlacement,
35}
36
37#[derive(Clone, PartialEq, Eq)]
38pub struct SymbolPathAttr {
39 pub path: SymbolPath,
40 pub user: SymbolUseRef,
41}
42
43impl fmt::Display for SymbolPathAttr {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{}", &self.path)
46 }
47}
48
49impl crate::formatter::PrettyPrint for SymbolPathAttr {
50 fn render(&self) -> crate::formatter::Document {
51 crate::formatter::display(self)
52 }
53}
54
55impl fmt::Debug for SymbolPathAttr {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.debug_struct("SymbolPathAttr")
58 .field("path", &self.path)
59 .field("user", &self.user.borrow())
60 .finish()
61 }
62}
63
64impl core::hash::Hash for SymbolPathAttr {
65 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
66 self.path.hash(state);
67 self.user.hash(state);
68 }
69}
70
71define_attr_type!(SymbolPathAttr);
72
73#[derive(Clone)]
121pub struct SymbolPath {
122 pub path: SmallVec<[SymbolNameComponent; 3]>,
124}
125
126impl FromIterator<SymbolNameComponent> for SymbolPath {
127 fn from_iter<I>(iter: I) -> Self
128 where
129 I: IntoIterator<Item = SymbolNameComponent>,
130 {
131 Self {
132 path: SmallVec::from_iter(iter),
133 }
134 }
135}
136
137impl SymbolPath {
138 pub fn new<I>(components: I) -> Result<Self, InvalidSymbolPathError>
139 where
140 I: IntoIterator<Item = SymbolNameComponent>,
141 {
142 let mut path = SmallVec::default();
143
144 let mut components = components.into_iter();
145
146 match components.next() {
147 None => return Err(InvalidSymbolPathError::Empty),
148 Some(component @ (SymbolNameComponent::Root | SymbolNameComponent::Component(_))) => {
149 path.push(component);
150 }
151 Some(component @ SymbolNameComponent::Leaf(_)) => {
152 if components.next().is_some() {
153 return Err(InvalidSymbolPathError::UnexpectedTrailingComponents);
154 }
155 path.push(component);
156 return Ok(Self { path });
157 }
158 };
159
160 while let Some(component) = components.next() {
161 match component {
162 SymbolNameComponent::Root => {
163 return Err(InvalidSymbolPathError::UnexpectedRootPlacement);
164 }
165 component @ SymbolNameComponent::Component(_) => {
166 path.push(component);
167 }
168 component @ SymbolNameComponent::Leaf(_) => {
169 path.push(component);
170 if components.next().is_some() {
171 return Err(InvalidSymbolPathError::UnexpectedTrailingComponents);
172 }
173 }
174 }
175 }
176
177 Ok(Self { path })
178 }
179
180 pub fn from_masm_function_id(id: FunctionIdent) -> Self {
203 let mut path = Self::from_masm_module_id(id.module.as_str());
204 path.path.push(SymbolNameComponent::Leaf(id.function.as_symbol()));
205 path
206 }
207
208 pub fn from_masm_module_id(id: &str) -> Self {
226 let parts = id.split("::");
227 Self::from_iter(
228 core::iter::once(SymbolNameComponent::Root)
229 .chain(parts.map(SymbolName::intern).map(SymbolNameComponent::Component)),
230 )
231 }
232
233 pub fn name(&self) -> SymbolName {
235 match self.path.last().expect("expected non-empty symbol path") {
236 SymbolNameComponent::Leaf(name) => *name,
237 component => panic!("invalid symbol path: expected leaf node, got: {component:?}"),
238 }
239 }
240
241 pub fn set_name(&mut self, name: SymbolName) {
243 match self.path.last_mut() {
244 Some(SymbolNameComponent::Leaf(ref mut prev_name)) => {
245 *prev_name = name;
246 }
247 _ => {
248 self.path.push(SymbolNameComponent::Leaf(name));
249 }
250 }
251 }
252
253 pub fn namespace(&self) -> Option<SymbolName> {
255 if self.is_absolute() {
256 match self.path[1] {
257 SymbolNameComponent::Component(ns) => Some(ns),
258 SymbolNameComponent::Leaf(_) => None,
259 SymbolNameComponent::Root => unreachable!(
260 "malformed symbol path: root components may only occur at the start of a path"
261 ),
262 }
263 } else {
264 None
265 }
266 }
267
268 pub fn to_library_path(&self) -> midenc_session::LibraryPath {
270 use midenc_session::{
271 miden_assembly::{ast::Ident, SourceSpan, Span},
272 LibraryNamespace, LibraryPath,
273 };
274
275 let mut components = self.path.iter();
276 let mut parts = SmallVec::<[_; 3]>::default();
277 if self.is_absolute() {
278 let _ = components.next();
279 }
280 let ns = match components.next() {
281 None => {
282 return LibraryPath::new_from_components(LibraryNamespace::Anon, parts);
283 }
284 Some(component) => LibraryNamespace::from_ident_unchecked(Ident::from_raw_parts(
285 Span::new(SourceSpan::default(), component.as_symbol_name().as_str().into()),
286 )),
287 };
288
289 for component in components {
290 let id = Ident::from_raw_parts(Span::new(
291 SourceSpan::default(),
292 component.as_symbol_name().as_str().into(),
293 ));
294 parts.push(id);
295 }
296
297 LibraryPath::new_from_components(ns, parts)
298 }
299
300 pub fn is_absolute(&self) -> bool {
302 matches!(&self.path[0], SymbolNameComponent::Root)
303 }
304
305 pub fn has_parent(&self) -> bool {
307 if self.is_absolute() {
308 self.path.len() > 2
309 } else {
310 self.path.len() > 1
311 }
312 }
313
314 pub fn is_prefix_of(&self, other: &Self) -> bool {
320 other.is_prefixed_by(&self.path)
321 }
322
323 pub fn is_prefixed_by(&self, prefix: &[SymbolNameComponent]) -> bool {
329 let mut a = prefix.iter();
330 let mut b = self.path.iter();
331
332 let mut index = 0;
333 loop {
334 match (a.next(), b.next()) {
335 (Some(part_a), Some(part_b)) if part_a == part_b => {
336 index += 1;
337 }
338 (None, Some(_)) => break index > 0,
339 _ => break false,
340 }
341 }
342 }
343
344 pub fn components(&self) -> impl ExactSizeIterator<Item = SymbolNameComponent> + '_ {
346 self.path.iter().copied()
347 }
348
349 pub fn parent(&self) -> Option<SymbolPath> {
351 match self.path.split_last()? {
352 (SymbolNameComponent::Root, []) => None,
353 (_, rest) => Some(SymbolPath {
354 path: SmallVec::from_slice(rest),
355 }),
356 }
357 }
358
359 pub fn without_leaf(&self) -> Cow<'_, SymbolPath> {
361 match self.path.split_last() {
362 Some((SymbolNameComponent::Leaf(_), rest)) => Cow::Owned(SymbolPath {
363 path: SmallVec::from_slice(rest),
364 }),
365 _ => Cow::Borrowed(self),
366 }
367 }
368}
369
370impl fmt::Display for SymbolPath {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 use core::fmt::Write;
388
389 let mut components = self.path.iter();
390
391 if self.is_absolute() {
392 let _ = components.next();
393 }
394
395 match components.next() {
396 Some(component) => f.write_str(component.as_symbol_name().as_str())?,
397 None => return Ok(()),
398 }
399 for component in components {
400 f.write_char('/')?;
401 f.write_str(component.as_symbol_name().as_str())?;
402 }
403 Ok(())
404 }
405}
406
407impl fmt::Debug for SymbolPath {
408 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
409 f.debug_struct("SymbolPath")
410 .field_with("path", |f| f.debug_list().entries(self.path.iter()).finish())
411 .finish()
412 }
413}
414impl crate::formatter::PrettyPrint for SymbolPath {
415 fn render(&self) -> crate::formatter::Document {
416 use crate::formatter::*;
417 display(self)
418 }
419}
420impl Eq for SymbolPath {}
421impl PartialEq for SymbolPath {
422 fn eq(&self, other: &Self) -> bool {
423 self.path == other.path
424 }
425}
426impl PartialOrd for SymbolPath {
427 #[inline]
428 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
429 Some(self.cmp(other))
430 }
431}
432impl Ord for SymbolPath {
433 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
434 self.path.cmp(&other.path)
435 }
436}
437impl core::hash::Hash for SymbolPath {
438 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
439 self.path.hash(state);
440 }
441}
442
443#[derive(Copy, Clone, PartialEq, Eq, Hash)]
452pub enum SymbolNameComponent {
453 Root,
455 Component(SymbolName),
457 Leaf(SymbolName),
459}
460
461impl SymbolNameComponent {
462 pub fn as_symbol_name(&self) -> SymbolName {
463 match self {
464 Self::Root => interner::symbols::Empty,
465 Self::Component(name) | Self::Leaf(name) => *name,
466 }
467 }
468
469 #[inline]
470 pub fn is_root(&self) -> bool {
471 matches!(self, Self::Root)
472 }
473
474 #[inline]
475 pub fn is_leaf(&self) -> bool {
476 matches!(self, Self::Leaf(_))
477 }
478}
479
480impl fmt::Debug for SymbolNameComponent {
481 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
482 match self {
483 Self::Root => f.write_str("Root"),
484 Self::Component(name) => {
485 f.debug_tuple("Component").field_with(|f| f.write_str(name.as_str())).finish()
486 }
487 Self::Leaf(name) => {
488 f.debug_tuple("Leaf").field_with(|f| f.write_str(name.as_str())).finish()
489 }
490 }
491 }
492}
493
494impl Ord for SymbolNameComponent {
495 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
496 use core::cmp::Ordering;
497
498 if self == other {
499 return Ordering::Equal;
500 }
501
502 match (self, other) {
503 (Self::Root, _) => Ordering::Less,
504 (_, Self::Root) => Ordering::Greater,
505 (Self::Component(x), Self::Component(y)) => x.cmp(y),
506 (Self::Component(_), _) => Ordering::Less,
507 (_, Self::Component(_)) => Ordering::Greater,
508 (Self::Leaf(x), Self::Leaf(y)) => x.cmp(y),
509 }
510 }
511}
512
513impl PartialOrd for SymbolNameComponent {
514 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
515 Some(self.cmp(other))
516 }
517}
518
519pub struct SymbolNameComponents {
521 parts: VecDeque<&'static str>,
522 name: SymbolName,
523 absolute: bool,
524 done: bool,
525}
526
527impl SymbolNameComponents {
528 pub fn from_component_model_symbol(symbol: SymbolName) -> Result<Self, crate::Report> {
555 use core::{iter::Peekable, str::CharIndices};
556
557 let mut parts = VecDeque::default();
558 if symbol == interner::symbols::Empty {
559 let done = symbol == interner::symbols::Empty;
560 return Ok(Self {
561 parts,
562 name: symbol,
563 done,
564 absolute: false,
565 });
566 }
567
568 #[inline(always)]
569 fn is_valid_id_char(c: char) -> bool {
570 c.is_ascii_alphanumeric() || c == '-'
571 }
572
573 fn lex_id<'a>(
574 s: &'a str,
575 start: usize,
576 lexer: &mut Peekable<CharIndices<'a>>,
577 ) -> Option<(usize, &'a str)> {
578 let mut end = start;
579 while let Some((i, c)) = lexer.next_if(|(_, c)| is_valid_id_char(*c)) {
580 end = i + c.len_utf8();
581 }
582 if end == start {
583 return None;
584 }
585 Some((end, unsafe { core::str::from_utf8_unchecked(&s.as_bytes()[start..end]) }))
586 }
587
588 let input = symbol.as_str();
589 let mut chars = input.char_indices().peekable();
590 let mut pos = 0;
591
592 let mut absolute = false;
594 let package_end = loop {
595 let (new_pos, _) = lex_id(input, pos, &mut chars).ok_or_else(|| {
596 crate::Report::msg(format!(
597 "invalid component model symbol: '{symbol}' contains invalid characters"
598 ))
599 })?;
600 pos = new_pos;
601
602 if let Some((new_pos, c)) = chars.next_if(|(_, c)| *c == ':') {
603 pos = new_pos + c.len_utf8();
604 absolute = true;
605 } else {
606 break pos;
607 }
608 };
609
610 if chars.peek().is_none() {
612 let symbol =
613 unsafe { core::str::from_utf8_unchecked(&input.as_bytes()[pos..package_end]) };
614 return Ok(Self {
615 parts,
616 name: SymbolName::intern(symbol),
617 done: false,
618 absolute,
619 });
620 }
621
622 let package_name =
624 unsafe { core::str::from_utf8_unchecked(&input.as_bytes()[pos..package_end]) };
625 parts.push_back(package_name);
626
627 match chars.next_if(|(_, c)| *c == '/') {
631 None => {
632 if chars.next_if(|(_, c)| *c == '@').is_some() {
635 if !absolute {
636 return Err(crate::Report::msg(
637 "invalid component model symbol: unqualified symbols cannot be \
638 versioned",
639 ));
640 }
641 parts.clear();
645 return Ok(Self {
646 parts,
647 name: SymbolName::intern(package_name),
648 done: false,
649 absolute,
650 });
651 } else {
652 return Err(crate::Report::msg(format!(
653 "invalid component model symbol: unexpected character in '{symbol}' \
654 starting at byte {pos}"
655 )));
656 }
657 }
658 Some((new_pos, c)) => {
659 pos = new_pos + c.len_utf8();
660 }
661 }
662
663 loop {
665 let (new_pos, id) = lex_id(input, pos, &mut chars).ok_or_else(|| {
666 crate::Report::msg(format!(
667 "invalid component model symbol: '{symbol}' contains invalid characters"
668 ))
669 })?;
670 pos = new_pos;
671
672 if let Some((new_pos, c)) = chars.next_if(|(_, c)| *c == '/') {
673 pos = new_pos + c.len_utf8();
674 parts.push_back(id);
675 } else {
676 break;
677 }
678 }
679
680 if chars.next_if(|(_, c)| *c == '@').is_some() {
686 let name = SymbolName::intern(parts.pop_back().unwrap());
687 return Ok(Self {
688 parts,
689 name,
690 done: false,
691 absolute,
692 });
693 }
694
695 if chars.peek().is_none() {
697 let name = SymbolName::intern(parts.pop_back().unwrap());
698 Ok(Self {
699 parts,
700 name,
701 done: false,
702 absolute,
703 })
704 } else {
705 Err(crate::Report::msg(format!(
706 "invalid component model symbol: '{symbol}' contains invalid character starting \
707 at byte {pos}"
708 )))
709 }
710 }
711
712 pub fn into_symbol_name(self) -> Option<SymbolName> {
716 let attr = self.into_symbol_path()?;
717
718 Some(SymbolName::intern(attr))
719 }
720
721 pub fn into_symbol_path(self) -> Option<SymbolPath> {
726 if self.name == interner::symbols::Empty {
727 return None;
728 }
729
730 if self.parts.is_empty() {
731 return Some(SymbolPath {
732 path: smallvec![SymbolNameComponent::Leaf(self.name)],
733 });
734 }
735
736 let mut path = SmallVec::<[_; 3]>::with_capacity(self.parts.len() + 1);
738
739 let mut parts = self.parts.into_iter();
741 if let Some(part) = parts.next() {
742 if part == "::" {
743 path.push(SymbolNameComponent::Root);
744 } else {
745 path.push(SymbolNameComponent::Component(SymbolName::intern(part)));
746 }
747 }
748
749 path.extend(parts.map(SymbolName::intern).map(SymbolNameComponent::Component));
751
752 path.push(SymbolNameComponent::Leaf(self.name));
754
755 Some(SymbolPath { path })
756 }
757}
758
759impl core::iter::FusedIterator for SymbolNameComponents {}
760impl Iterator for SymbolNameComponents {
761 type Item = SymbolNameComponent;
762
763 fn next(&mut self) -> Option<Self::Item> {
764 if self.done {
765 return None;
766 }
767 if self.absolute {
768 self.absolute = false;
769 return Some(SymbolNameComponent::Root);
770 }
771 if let Some(part) = self.parts.pop_front() {
772 return Some(SymbolNameComponent::Component(part.into()));
773 }
774 self.done = true;
775 Some(SymbolNameComponent::Leaf(self.name))
776 }
777}
778impl ExactSizeIterator for SymbolNameComponents {
779 fn len(&self) -> usize {
780 let is_empty = self.name == interner::symbols::Empty;
781 if is_empty {
782 assert_eq!(self.parts.len(), 0, "malformed symbol name components");
783 0
784 } else {
785 self.parts.len() + 1
786 }
787 }
788}