use std::collections::HashMap;
use thiserror::Error;
use galvan_ast::{Ast, FnDecl, Ident, MainDecl, RootItem, TypeDecl, TypeIdent};
pub struct LookupContext<'a> {
pub types: HashMap<TypeId, &'a TypeDecl>,
pub functions: HashMap<FunctionId, &'a FnDecl>,
pub main: Option<&'a MainDecl>,
}
#[derive(Debug, Error)]
pub enum LookupError {
#[error("Type not found")]
TypeNotFound,
#[error("Function not found")]
FunctionNotFound,
#[error("Duplicate main function")]
DuplicateMain,
#[error("Duplicate type")]
DuplicateType,
#[error("Duplicate function")]
DuplicateFunction,
}
impl<'a> LookupContext<'a> {
pub fn new(asts: &'a [Ast]) -> Result<Self, LookupError> {
let mut types = HashMap::new();
let mut functions = HashMap::new();
let mut main = None;
for ast in asts {
for top in &ast.toplevel {
match top {
RootItem::Type(type_decl) => {
types.insert(type_decl.ident().into(), type_decl);
}
RootItem::Fn(fn_decl) => {
let func_id = FunctionId::new(None, &fn_decl.signature.identifier, &[]);
functions.insert(func_id, fn_decl);
}
RootItem::Test(_) => {}
RootItem::Main(m) => {
if main.is_some() {
return Err(LookupError::DuplicateMain);
}
main = Some(m);
}
}
}
}
Ok(LookupContext {
types,
functions,
main,
})
}
}
impl LookupContext<'_> {
pub fn resolve_type(&self, name: &TypeIdent) -> Option<&TypeDecl> {
self.types.get(&name.into()).copied()
}
pub fn resolve_function(
&self,
receiver: Option<&TypeIdent>,
name: &Ident,
labels: &[&str],
) -> Option<&FnDecl> {
let func_id = FunctionId::new(receiver, name, labels);
self.functions.get(&func_id).copied()
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct TypeId(Box<str>);
impl<S> From<S> for TypeId
where
S: AsRef<str>,
{
fn from(ident: S) -> Self {
Self(ident.as_ref().into())
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
struct FunctionId(Box<str>);
impl FunctionId {
fn new(receiver: Option<&TypeIdent>, fn_ident: &Ident, labels: &[&str]) -> Self {
let mut id = String::new();
if let Some(receiver) = receiver {
id.push_str(receiver.as_str());
id.push_str("::");
}
id.push_str(fn_ident.as_str());
if !labels.is_empty() {
id.push(':');
id.push_str(&labels.join(":"));
}
Self(id.into())
}
}