1use std::sync::Arc;
2
3use gramatika::{Spanned, Substr, Token as _};
4
5use crate::{
6 decl::{
7 Decl, ImportDecl, ImportPath, ImportPathBlock, ImportPathDecl, ImportPathLeaf,
8 NamespacedImportPath,
9 },
10 token::Token,
11 traversal::{FlowControl, Visitor, Walk},
12 utils, SyntaxTree,
13};
14
15#[derive(Clone, DebugLisp, PartialEq, Eq, Hash)]
16pub struct ImportedSymbol {
17 pub qualified_path: Arc<[Token]>,
18 pub local_binding: Token,
19}
20
21impl SyntaxTree {
22 pub fn find_exported_symbol<'a>(&'a self, needle: &Token) -> Option<&'a Decl> {
23 self.inner.iter().find(|decl| {
24 decl.name().lexeme() == needle.lexeme()
25 && (needle.kind() == decl.name().kind() || !matches!(decl, Decl::ImportPath(_)))
26 })
27 }
28
29 pub fn import_path_decl(&self) -> Option<&Decl> {
30 self.inner
31 .iter()
32 .find(|decl| matches!(decl, Decl::ImportPath(_)))
33 }
34}
35
36impl ImportPathDecl {
37 pub fn qualified_path(&self) -> Arc<[Token]> {
46 let mut result = vec![];
47 let mut import_path = &self.path;
48 loop {
49 match import_path {
50 ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
51 result.push(namespace.clone());
52 import_path = path.as_ref();
53 }
54 ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
55 result.push(name.clone());
56 break result.into();
57 }
58 ImportPath::Block(_) => unreachable!(),
59 }
60 }
61 }
62
63 pub fn qualified_name(&self) -> Token {
77 let mut result = Substr::new();
78 let mut import_path = &self.path;
79 loop {
80 match import_path {
81 ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
82 if result.is_empty() {
83 (result, _) = namespace.as_inner();
84 } else {
85 let (next, _) = namespace.as_inner();
86 result = utils::join_substrs(&result, &next);
87 }
88
89 import_path = path.as_ref();
90 }
91 ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
92 if result.is_empty() {
93 (result, _) = name.as_inner();
94 } else {
95 let (next, _) = name.as_inner();
96 result = utils::join_substrs(&result, &next);
97 }
98
99 break;
100 }
101 ImportPath::Block(_) => unreachable!(),
102 }
103 }
104
105 Token::Path(result, self.path.span())
106 }
107}
108
109impl ImportDecl {
110 pub fn build_imported_symbols(&self) -> Vec<ImportedSymbol> {
111 let mut builder = ImportedSymbolsBuilder::default();
112 self.walk(&mut builder);
113
114 let mut result = builder.build();
115 result.sort_unstable_by_key(|sym| sym.local_binding.span());
116
117 result
118 }
119}
120
121#[derive(Default)]
122struct ImportedSymbolsBuilder {
123 path: Vec<Token>,
124 binding: Option<Token>,
125 forks: Vec<ImportedSymbolsBuilder>,
126}
127
128impl ImportedSymbolsBuilder {
129 fn build(self) -> Vec<ImportedSymbol> {
130 std::iter::once(ImportedSymbol {
131 qualified_path: self.path.into(),
132 local_binding: self.binding.unwrap(),
133 })
134 .chain(self.forks.into_iter().flat_map(|builder| builder.build()))
135 .collect()
136 }
137
138 fn fork(&self) -> Self {
139 Self {
140 path: self.path.clone(),
141 binding: None,
142 forks: vec![],
143 }
144 }
145}
146
147impl Visitor for ImportedSymbolsBuilder {
148 fn visit_import_path(&mut self, path: &ImportPath) -> FlowControl {
149 match path {
150 ImportPath::Leaf(ImportPathLeaf { name, as_binding }) => {
151 self.path.push(name.clone());
152 self.binding = Some(as_binding.as_ref().cloned().unwrap_or(name.clone()));
153
154 FlowControl::Break
155 }
156 ImportPath::Namespaced(NamespacedImportPath { namespace, .. }) => {
157 self.path.push(namespace.clone());
158
159 FlowControl::Continue
160 }
161 ImportPath::Block(ImportPathBlock { paths, .. }) => {
162 for (idx, path) in paths.iter().enumerate().rev() {
163 if idx == 0 {
164 path.walk(self);
165 } else {
166 let mut fork = self.fork();
167 path.walk(&mut fork);
168 self.forks.push(fork);
169 }
170 }
171
172 FlowControl::Break
173 }
174 }
175 }
176}