use crate::output::diagnostic;
use crate::output::extension;
use crate::parse::context;
use crate::parse::traversal;
use std::collections::HashMap;
use std::sync::Arc;
pub type Reference<T> = Arc<Definition<T>>;
#[derive(Debug)]
pub struct Definition<T> {
members: HashMap<String, Vec<InternalReference<T>>>,
}
impl<T> Clone for Definition<T> {
fn clone(&self) -> Self {
Self {
members: self.members.clone(),
}
}
}
impl<T> Default for Definition<T> {
fn default() -> Self {
Self {
members: Default::default(),
}
}
}
impl<T> Definition<T> {
pub fn new() -> Self {
Default::default()
}
pub fn define_item<S: AsRef<str>>(&mut self, name: S, item: Arc<T>, public: bool) {
let name = name.as_ref();
self.members
.entry(name.to_ascii_lowercase())
.or_default()
.push(InternalReference {
member: Arc::new(Member::Item(item)),
original_name: name.to_string(),
public,
})
}
pub fn define_nested<S: AsRef<str>>(
&mut self,
name: S,
item: Option<Reference<T>>,
public: bool,
) {
let name = name.as_ref();
self.members
.entry(name.to_ascii_lowercase())
.or_default()
.push(InternalReference {
member: Arc::new(if let Some(item) = item {
Member::Nested(item)
} else {
Member::UnresolvedNested
}),
original_name: name.to_string(),
public,
})
}
fn visibility(
local: bool,
with_prefix: bool,
prefix_public: bool,
reference_public: bool,
) -> bool {
#[allow(clippy::if_same_then_else)]
#[allow(clippy::needless_bool)]
if local && !with_prefix {
true
} else if (prefix_public || !with_prefix) && reference_public {
true
} else {
false
}
}
fn resolve_internal(
&self,
target: &mut ResolutionResult<T>,
local: bool,
prefix: Option<&str>,
suffix: &str,
prefix_public: bool,
) {
if let Some(references) = self.members.get(&suffix.to_ascii_lowercase()) {
for reference in references {
let original_name = if let Some(prefix) = prefix {
format!("{prefix}.{}", reference.original_name)
} else {
reference.original_name.clone()
};
let visible =
Self::visibility(local, prefix.is_some(), prefix_public, reference.public);
let target_vec = if visible {
&mut target.visible
} else {
&mut target.invisible
};
target_vec.push((original_name, reference.member.clone()))
}
}
for (split_point, _) in suffix.char_indices().filter(|(_, c)| *c == '.') {
let namespace_name = &suffix[..split_point];
let new_suffix = &suffix[split_point + 1..];
if let Some(references) = self.members.get(&namespace_name.to_ascii_lowercase()) {
for reference in references {
match reference.member.as_ref() {
Member::Item(_) => (),
Member::Nested(nested) => {
let new_prefix = if let Some(prefix) = prefix {
format!("{prefix}.{}", reference.original_name)
} else {
reference.original_name.clone()
};
nested.resolve_internal(
target,
local,
Some(&new_prefix),
new_suffix,
prefix_public && reference.public,
);
}
Member::UnresolvedNested => {
let visible = Self::visibility(
local,
prefix.is_some(),
prefix_public,
reference.public,
);
if visible {
target.visible_incomplete = true;
} else {
target.invisible_incomplete = true;
};
}
}
}
}
}
}
pub fn resolve_local<R>(&self, reference: R) -> ResolutionResult<T>
where
R: Into<extension::reference::Data<T>>,
{
let reference = reference.into();
let name = reference.name.name().unwrap_or("!").to_string();
let mut result = ResolutionResult::new(reference);
self.resolve_internal(&mut result, true, None, &name, true);
result
}
pub fn resolve_public<R>(&self, reference: R) -> ResolutionResult<T>
where
R: Into<extension::reference::Data<T>>,
{
let reference = reference.into();
let name = reference.name.name().unwrap_or("!").to_string();
let mut result = ResolutionResult::new(reference);
self.resolve_internal(&mut result, false, None, &name, true);
result
}
}
#[derive(Debug)]
struct InternalReference<T> {
pub member: Arc<Member<T>>,
pub original_name: String,
pub public: bool,
}
impl<T> Clone for InternalReference<T> {
fn clone(&self) -> Self {
Self {
member: self.member.clone(),
original_name: self.original_name.clone(),
public: self.public,
}
}
}
#[derive(Debug)]
enum Member<T> {
Item(Arc<T>),
Nested(Reference<T>),
UnresolvedNested,
}
impl<T> Clone for Member<T> {
fn clone(&self) -> Self {
match self {
Self::Item(x) => Self::Item(x.clone()),
Self::Nested(x) => Self::Nested(x.clone()),
Self::UnresolvedNested => Self::UnresolvedNested,
}
}
}
impl<T> Member<T> {
pub fn as_item(&self) -> Option<Arc<T>> {
match self {
Member::Item(x) => Some(x.clone()),
_ => None,
}
}
pub fn as_namespace(&self) -> Option<Option<Reference<T>>> {
match self {
Member::Nested(x) => Some(Some(x.clone())),
Member::UnresolvedNested => Some(None),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct ResolutionResult<T> {
unresolved_reference: extension::reference::Data<T>,
visible: Vec<(String, Arc<Member<T>>)>,
visible_incomplete: bool,
invisible: Vec<(String, Arc<Member<T>>)>,
invisible_incomplete: bool,
filtered: bool,
}
impl<T> std::fmt::Display for ResolutionResult<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.unresolved_reference)?;
if self.visible_incomplete {
match self.visible.len() {
0 => write!(
f,
" (no known matching definitions, namespace not resolved)"
),
1 => write!(f, " (one or more matching definitions)"),
n => write!(f, " ({n} or more matching definitions)"),
}
} else {
match self.visible.len() {
0 => write!(f, " (no matching definitions)"),
1 => write!(f, " (one matching definition)"),
n => write!(f, " ({n} matching definitions)"),
}
}
}
}
impl<T> ResolutionResult<T> {
pub fn new(unresolved_reference: extension::reference::Data<T>) -> Self {
Self {
unresolved_reference,
visible: vec![],
visible_incomplete: true,
invisible: vec![],
invisible_incomplete: false,
filtered: false,
}
}
fn filter<F: Fn(&(String, Arc<Member<T>>)) -> bool>(mut self, filter: F) -> Self {
let size = self.visible.len();
self.visible.retain(&filter);
self.invisible.retain(&filter);
if self.visible.len() < size {
self.filtered = true;
}
self
}
pub fn filter_items<F: Fn(&T) -> bool>(self, filter: F) -> Self {
self.filter(|x| {
x.1.as_item()
.map(|x| filter(x.as_ref()))
.unwrap_or_default()
})
}
pub fn filter_all_items(self) -> Self {
self.filter(|x| x.1.as_item().is_some())
}
pub fn filter_namespaces(self) -> Self {
self.filter(|x| x.1.as_namespace().is_some())
}
fn expect<F1, F2>(
self,
parse_context: &mut context::Context,
if_not_applicable: F1,
if_ambiguous: F2,
allow_ambiguity: bool,
optional: bool,
) -> Self
where
F1: FnOnce(String, &mut context::Context) -> bool,
F2: FnOnce(String, &mut context::Context) -> bool,
{
let mut visible = self.visible.iter();
if visible.next().is_some() {
if visible.next().is_some()
&& !allow_ambiguity
&& !if_ambiguous(self.unresolved_reference.to_string(), parse_context)
{
traversal::push_diagnostic(
parse_context,
diagnostic::Level::Error,
cause!(
LinkAmbiguousName,
"multiple definitions are in scope for {}",
self.unresolved_reference
),
)
}
} else if self.visible_incomplete || optional {
} else if !self.invisible.is_empty() {
traversal::push_diagnostic(
parse_context,
diagnostic::Level::Error,
cause!(
LinkUnresolvedName,
"a definition for {} exists, but is not visible from here",
self.unresolved_reference
),
);
} else if self.invisible_incomplete {
traversal::push_diagnostic(
parse_context,
diagnostic::Level::Error,
cause!(
LinkUnresolvedName,
"a definition for {} may exist, but would not be visible from here",
self.unresolved_reference
),
);
} else if self.filtered {
if !if_not_applicable(self.unresolved_reference.to_string(), parse_context) {
traversal::push_diagnostic(
parse_context,
diagnostic::Level::Error,
cause!(
LinkUnresolvedName,
"a definition for {} exists, but it cannot be used in this context",
self.unresolved_reference
),
)
}
} else {
traversal::push_diagnostic(
parse_context,
diagnostic::Level::Error,
cause!(
LinkUnresolvedName,
"no definition found for {}",
self.unresolved_reference
),
);
}
self
}
pub fn expect_one<F1, F2>(
self,
parse_context: &mut context::Context,
if_not_applicable: F1,
if_ambiguous: F2,
) -> Self
where
F1: FnOnce(String, &mut context::Context) -> bool,
F2: FnOnce(String, &mut context::Context) -> bool,
{
self.expect(parse_context, if_not_applicable, if_ambiguous, false, false)
}
pub fn expect_not_ambiguous<F>(
self,
parse_context: &mut context::Context,
if_ambiguous: F,
) -> Self
where
F: FnOnce(String, &mut context::Context) -> bool,
{
self.expect(parse_context, |_, _| true, if_ambiguous, false, true)
}
pub fn expect_multiple<F>(
self,
parse_context: &mut context::Context,
if_not_applicable: F,
) -> Self
where
F: FnOnce(String, &mut context::Context) -> bool,
{
self.expect(parse_context, if_not_applicable, |_, _| true, true, false)
}
pub fn as_item(&self) -> extension::reference::Reference<T> {
let mut data = self.unresolved_reference.clone();
if let Some(item) = self.visible.iter().filter_map(|x| x.1.as_item()).next() {
data.definition.replace(item);
}
Arc::new(data)
}
pub fn as_opt_item(&self) -> Option<extension::reference::Reference<T>> {
self.visible
.iter()
.filter_map(|x| x.1.as_item())
.next()
.map(|item| {
let mut data = self.unresolved_reference.clone();
data.definition.replace(item);
Arc::new(data)
})
}
pub fn as_namespace(&self) -> Option<Reference<T>> {
self.visible
.iter()
.filter_map(|x| x.1.as_namespace())
.next()
.flatten()
}
pub fn expect_not_yet_defined(&self) -> diagnostic::Result<()> {
if self.visible.is_empty() {
Ok(())
} else {
Err(cause!(
LinkDuplicateDefinition,
"{} is already defined",
self.unresolved_reference
))
}
}
}