use anyhow::Result;
use enum_as_inner::EnumAsInner;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt::Debug};
use super::module::{Module, NS_DEFAULT_DB, NS_NO_RESOLVE, NS_STD};
use crate::ast::pl::*;
use crate::error::Span;
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct Context {
pub(crate) root_mod: Module,
pub(crate) span_map: HashMap<usize, Span>,
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct Decl {
pub declared_at: Option<usize>,
pub kind: DeclKind,
}
#[derive(Debug, Serialize, Deserialize, Clone, EnumAsInner)]
pub enum DeclKind {
Module(Module),
LayeredModules(Vec<Module>),
TableDecl(TableDecl),
Column(usize),
Wildcard(Box<DeclKind>),
FuncDef(FuncDef),
Expr(Box<Expr>),
NoResolve,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TableDecl {
pub frame: TableFrame,
pub expr: Option<Box<Expr>>,
}
#[derive(Clone, Default, Eq, Debug, PartialEq, Serialize, Deserialize)]
pub struct TableFrame {
pub columns: Vec<TableColumn>,
}
#[derive(Clone, Eq, Debug, PartialEq, Serialize, Deserialize)]
pub enum TableColumn {
Wildcard,
Single(Option<String>),
}
impl Context {
pub fn declare_func(&mut self, func_def: FuncDef, id: Option<usize>) {
let name = func_def.name.clone();
let path = vec![NS_STD.to_string()];
let ident = Ident { name, path };
let decl = Decl {
kind: DeclKind::FuncDef(func_def),
declared_at: id,
};
self.root_mod.insert(ident, decl).unwrap();
}
pub fn declare_table(&mut self, table_def: TableDef, id: Option<usize>) {
let name = table_def.name;
let path = vec![NS_DEFAULT_DB.to_string()];
let ident = Ident { name, path };
let frame = table_def.value.ty.clone().unwrap().into_table().unwrap();
let frame = TableFrame {
columns: (frame.columns.into_iter())
.map(|col| match col {
FrameColumn::Wildcard { .. } => TableColumn::Wildcard,
FrameColumn::Single { name, .. } => TableColumn::Single(name.map(|n| n.name)),
})
.collect(),
};
let expr = Some(table_def.value);
let decl = Decl {
declared_at: id,
kind: DeclKind::TableDecl(TableDecl { frame, expr }),
};
self.root_mod.insert(ident, decl).unwrap();
}
pub fn resolve_ident(&mut self, ident: &Ident) -> Result<Ident, String> {
if ident.name != "*" {
let decls = self.root_mod.lookup(ident);
match decls.len() {
0 => {}
1 => return Ok(decls.into_iter().next().unwrap()),
_ => {
let decls = decls.into_iter().map(|d| d.to_string()).join(", ");
return Err(format!("Ambiguous reference. Could be from any of {decls}"));
}
}
}
let decls = self.root_mod.lookup(&Ident {
path: ident.path.clone(),
name: "*".to_string(),
});
match decls.len() {
0 => Err(format!("Unknown name {ident}")),
1 => {
let wildcard_ident = decls.into_iter().next().unwrap();
let wildcard = self.root_mod.get(&wildcard_ident).unwrap();
let wildcard_default = wildcard.kind.as_wildcard().cloned().unwrap();
let module_ident = wildcard_ident.pop().unwrap();
let module = self.root_mod.get_mut(&module_ident).unwrap();
let module = module.kind.as_module_mut().unwrap();
module
.names
.insert(ident.name.clone(), Decl::from(*wildcard_default));
if let Some(table_ident) = module.instance_of_table.clone() {
log::debug!("infering {ident} to be from table {table_ident}");
self.infer_table_column(&table_ident, &ident.name)?;
}
Ok(module_ident + Ident::from_name(ident.name.clone()))
}
_ => {
let ident = NS_NO_RESOLVE.to_string();
self.root_mod
.names
.insert(ident, Decl::from(DeclKind::NoResolve));
log::debug!(
"... could either of {:?}",
decls.iter().map(|x| x.to_string()).collect_vec()
);
Ok(Ident::from_name(NS_NO_RESOLVE))
}
}
}
fn infer_table_column(&mut self, table_ident: &Ident, col_name: &str) -> Result<(), String> {
let table = self.root_mod.get_mut(table_ident).unwrap();
let table_decl = table.kind.as_table_decl_mut().unwrap();
let has_wildcard =
(table_decl.frame.columns.iter()).any(|c| matches!(c, TableColumn::Wildcard));
if !has_wildcard {
return Err(format!("Table {table_ident:?} does not have wildcard."));
}
let exists = table_decl.frame.columns.iter().any(|c| match c {
TableColumn::Single(Some(n)) => n == col_name,
_ => false,
});
if exists {
return Ok(());
}
let col = TableColumn::Single(Some(col_name.to_string()));
table_decl.frame.columns.push(col);
if let Some(expr) = &table_decl.expr {
if let Some(Ty::Table(frame)) = expr.ty.as_ref() {
let wildcard_inputs = (frame.columns.iter())
.filter_map(|c| c.as_wildcard())
.collect_vec();
match wildcard_inputs.len() {
0 => return Err(format!("Cannot infer where {table_ident}.{col_name} is from")),
1 => {
let input_name = wildcard_inputs.into_iter().next().unwrap();
let input = frame.find_input(input_name).unwrap();
let table_ident = input.table.clone();
self.infer_table_column(&table_ident, col_name)?;
}
_ => {
return Err(format!("Cannot infer where {table_ident}.{col_name} is from. It could be any of {wildcard_inputs:?}"))
}
}
}
}
Ok(())
}
}
impl Default for DeclKind {
fn default() -> Self {
DeclKind::Module(Module::default())
}
}
impl Debug for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.root_mod.fmt(f)
}
}
impl From<DeclKind> for Decl {
fn from(kind: DeclKind) -> Self {
Decl {
kind,
declared_at: None,
}
}
}
impl std::fmt::Display for Decl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.kind, f)
}
}
impl std::fmt::Display for DeclKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Module(arg0) => f.debug_tuple("Module").field(arg0).finish(),
Self::LayeredModules(arg0) => f.debug_tuple("LayeredModules").field(arg0).finish(),
Self::TableDecl(TableDecl { frame, expr }) => write!(f, "TableDef: {frame} {expr:?}"),
Self::Column(arg0) => write!(f, "Column (target {arg0})"),
Self::Wildcard(arg0) => write!(f, "Wildcard (default: {arg0})"),
Self::FuncDef(arg0) => write!(f, "FuncDef: {arg0}"),
Self::Expr(arg0) => write!(f, "Expr: {arg0}"),
Self::NoResolve => write!(f, "NoResolve"),
}
}
}
impl std::fmt::Display for TableFrame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("[")?;
for (index, col) in self.columns.iter().enumerate() {
let is_last = index == self.columns.len() - 1;
let col = match col {
TableColumn::Wildcard => "*",
TableColumn::Single(name) => name.as_deref().unwrap_or("<unnamed>"),
};
f.write_str(col)?;
if !is_last {
f.write_str(", ")?;
}
}
f.write_str("]")
}
}