use std::collections::HashSet;
use itertools::Itertools;
use super::Resolver;
use crate::ir::decl::{Decl, DeclKind, Module};
use crate::ir::pl::{Expr, ExprKind};
use crate::pr::Ident;
use crate::semantic::{NS_INFER, NS_INFER_MODULE, NS_SELF, NS_THAT, NS_THIS};
use crate::Error;
use crate::Result;
use crate::WithErrorInfo;
impl Resolver<'_> {
pub(super) fn resolve_ident(&mut self, ident: &Ident) -> Result<Ident, Error> {
let mut res = if let Some(default_namespace) = self.default_namespace.clone() {
self.resolve_ident_core(ident, Some(&default_namespace))
} else {
let mut ident = ident.clone().prepend(self.current_module_path.clone());
let mut res = self.resolve_ident_core(&ident, None);
for _ in 0..self.current_module_path.len() {
if res.is_ok() {
break;
}
ident = ident.pop_front().1.unwrap();
res = self.resolve_ident_core(&ident, None);
}
res
};
match &res {
Ok(fq_ident) => {
let decl = self.root_mod.module.get(fq_ident).unwrap();
if let DeclKind::Import(target) = &decl.kind {
let target = target.clone();
return self.resolve_ident(&target);
}
}
Err(e) => {
log::debug!(
"cannot resolve `{ident}`: `{e:?}`, root_mod={:#?}",
self.root_mod
);
let mut available_names = Vec::new();
available_names.extend(self.collect_columns_in_module(NS_THIS));
available_names.extend(self.collect_columns_in_module(NS_THAT));
if !available_names.is_empty() {
let available_names = available_names.iter().map(Ident::to_string).join(", ");
res = res.push_hint(format!("available columns: {available_names}"));
}
}
}
res
}
fn collect_columns_in_module(&mut self, mod_name: &str) -> Vec<Ident> {
let mut cols = Vec::new();
let Some(module) = self.root_mod.module.names.get(mod_name) else {
return cols;
};
let DeclKind::Module(this) = &module.kind else {
return cols;
};
for (ident, decl) in this.as_decls().into_iter().sorted_by_key(|x| x.1.order) {
if let DeclKind::Column(_) = decl.kind {
cols.push(ident);
}
}
cols
}
pub(super) fn resolve_ident_core(
&mut self,
ident: &Ident,
default_namespace: Option<&String>,
) -> Result<Ident, Error> {
if ident.name == "*" {
let wildcard_ident = match (ident.path.is_empty(), default_namespace) {
(true, Some(ns)) => ident.clone().prepend(vec![ns.clone()]),
_ => ident.clone(),
};
return self
.resolve_ident_wildcard(&wildcard_ident)
.map_err(Error::new_simple);
}
let decls = self.root_mod.module.lookup(ident);
match decls.len() {
0 => {}
1 => return Ok(decls.into_iter().next().unwrap()),
_ => return Err(ambiguous_error(decls, None)),
}
let ident = if let Some(default_namespace) = default_namespace {
let ident = ident.clone().prepend(vec![default_namespace.clone()]);
let decls = self.root_mod.module.lookup(&ident);
match decls.len() {
0 => ident,
1 => return Ok(decls.into_iter().next().unwrap()),
_ => return Err(ambiguous_error(decls, None)),
}
} else {
ident.clone()
};
match self.resolve_ident_fallback(&ident, NS_INFER) {
Ok(inferred_ident) => Ok(inferred_ident),
Err(None) => Err(Error::new_simple(
format!("Unknown name `{}`", &ident).to_string(),
)),
Err(Some(msg)) => Err(msg),
}
}
fn resolve_ident_fallback(
&mut self,
ident: &Ident,
name_replacement: &'static str,
) -> Result<Ident, Option<Error>> {
let infer_ident = ident.clone().with_name(name_replacement);
let mut decls = self.root_mod.module.lookup(&infer_ident);
if decls.is_empty() {
if let Some(parent) = infer_ident.clone().pop() {
let _ = self.resolve_ident_fallback(&parent, NS_INFER_MODULE)?;
decls = self.root_mod.module.lookup(&infer_ident)
}
}
match decls.len() {
1 => {
let infer_ident = decls.into_iter().next().unwrap();
self.infer_decl(infer_ident, ident)
.map_err(|x| Some(Error::new_simple(x)))
}
0 => Err(None),
_ => Err(Some(ambiguous_error(decls, Some(&ident.name)))),
}
}
fn infer_decl(&mut self, infer_ident: Ident, original: &Ident) -> Result<Ident, String> {
let infer = self.root_mod.module.get(&infer_ident).unwrap();
let mut infer_default = *infer.kind.as_infer().cloned().unwrap();
if let DeclKind::Module(new_module) = &mut infer_default {
*new_module = Module::new_database();
}
let module_ident = infer_ident.pop().unwrap();
let module = self.root_mod.module.get_mut(&module_ident).unwrap();
let module = module.kind.as_module_mut().unwrap();
module
.names
.insert(original.name.clone(), Decl::from(infer_default));
if let Some(decl) = module.names.get(NS_SELF).cloned() {
if let DeclKind::InstanceOf(table_ident, _) = decl.kind {
log::debug!("inferring {original} to be from table {table_ident}");
self.infer_table_column(&table_ident, &original.name)?;
}
}
Ok(module_ident + Ident::from_name(original.name.clone()))
}
fn resolve_ident_wildcard(&mut self, ident: &Ident) -> Result<Ident, String> {
let ident_self = ident.clone().pop().ok_or_else(|| {
"Column wildcard `*` must be qualified, e.g. `table_name.*`".to_string()
})? + Ident::from_name(NS_SELF);
let mut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self) {
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1 {
return Err(format!("Unknown relation {ident}"));
}
let module_fq_self = res.into_iter().next().unwrap();
let fields = self.construct_wildcard_include(&module_fq_self);
let cols_expr = Expr {
flatten: true,
..Expr::new(ExprKind::Tuple(fields))
};
let cols_expr = DeclKind::Expr(Box::new(cols_expr));
let save_as = "_wildcard_match";
self.root_mod
.module
.names
.insert(save_as.to_string(), cols_expr.into());
Ok(Ident::from_name(save_as))
}
}
fn ambiguous_error(idents: HashSet<Ident>, replace_name: Option<&String>) -> Error {
let all_this = idents.iter().all(|d| d.starts_with_part(NS_THIS));
let mut chunks = Vec::new();
for mut ident in idents {
if all_this {
let (_, rem) = ident.pop_front();
if let Some(rem) = rem {
ident = rem;
} else {
continue;
}
}
if let Some(name) = replace_name {
ident.name.clone_from(name);
}
chunks.push(ident.to_string());
}
chunks.sort();
let hint = format!("could be any of: {}", chunks.join(", "));
Error::new_simple("Ambiguous name").push_hint(hint)
}