use std::sync::Arc;
use gramatika::{Spanned, Substr, Token as _};
use crate::{
decl::{
Decl, ImportDecl, ImportPath, ImportPathBlock, ImportPathDecl, ImportPathLeaf,
NamespacedImportPath,
},
token::Token,
traversal::{FlowControl, Visitor, Walk},
utils, SyntaxTree,
};
#[derive(Clone, DebugLisp, PartialEq, Eq, Hash)]
pub struct ImportedSymbol {
pub qualified_path: Arc<[Token]>,
pub local_binding: Token,
}
impl SyntaxTree {
pub fn find_exported_symbol<'a>(&'a self, needle: &Token) -> Option<&'a Decl> {
self.inner.iter().find(|decl| {
decl.name().lexeme() == needle.lexeme()
&& (needle.kind() == decl.name().kind() || !matches!(decl, Decl::ImportPath(_)))
})
}
pub fn import_path_decl(&self) -> Option<&Decl> {
self.inner
.iter()
.find(|decl| matches!(decl, Decl::ImportPath(_)))
}
}
impl ImportPathDecl {
pub fn qualified_path(&self) -> Arc<[Token]> {
let mut result = vec![];
let mut import_path = &self.path;
loop {
match import_path {
ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
result.push(namespace.clone());
import_path = path.as_ref();
}
ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
result.push(name.clone());
break result.into();
}
ImportPath::Block(_) => unreachable!(),
}
}
}
pub fn qualified_name(&self) -> Token {
let mut result = Substr::new();
let mut import_path = &self.path;
loop {
match import_path {
ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
if result.is_empty() {
(result, _) = namespace.as_inner();
} else {
let (next, _) = namespace.as_inner();
result = utils::join_substrs(&result, &next);
}
import_path = path.as_ref();
}
ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
if result.is_empty() {
(result, _) = name.as_inner();
} else {
let (next, _) = name.as_inner();
result = utils::join_substrs(&result, &next);
}
break;
}
ImportPath::Block(_) => unreachable!(),
}
}
Token::Path(result, self.path.span())
}
}
impl ImportDecl {
pub fn build_imported_symbols(&self) -> Vec<ImportedSymbol> {
let mut builder = ImportedSymbolsBuilder::default();
self.walk(&mut builder);
let mut result = builder.build();
result.sort_unstable_by_key(|sym| sym.local_binding.span());
result
}
}
#[derive(Default)]
struct ImportedSymbolsBuilder {
path: Vec<Token>,
binding: Option<Token>,
forks: Vec<ImportedSymbolsBuilder>,
}
impl ImportedSymbolsBuilder {
fn build(self) -> Vec<ImportedSymbol> {
std::iter::once(ImportedSymbol {
qualified_path: self.path.into(),
local_binding: self.binding.unwrap(),
})
.chain(self.forks.into_iter().flat_map(|builder| builder.build()))
.collect()
}
fn fork(&self) -> Self {
Self {
path: self.path.clone(),
binding: None,
forks: vec![],
}
}
}
impl Visitor for ImportedSymbolsBuilder {
fn visit_import_path(&mut self, path: &ImportPath) -> FlowControl {
match path {
ImportPath::Leaf(ImportPathLeaf { name, as_binding }) => {
self.path.push(name.clone());
self.binding = Some(as_binding.as_ref().cloned().unwrap_or(name.clone()));
FlowControl::Break
}
ImportPath::Namespaced(NamespacedImportPath { namespace, .. }) => {
self.path.push(namespace.clone());
FlowControl::Continue
}
ImportPath::Block(ImportPathBlock { paths, .. }) => {
for (idx, path) in paths.iter().enumerate().rev() {
if idx == 0 {
path.walk(self);
} else {
let mut fork = self.fork();
path.walk(&mut fork);
self.forks.push(fork);
}
}
FlowControl::Break
}
}
}
}