eqlog 0.9.0

Datalog with equality
Documentation
use convert_case::{Case::Snake, Casing as _};

use crate::algebra::signature::{FuncId, Signature, TypeId, TypeKind};
use crate::ast::Ast;
use crate::flat_eqlog::FlatRel;

pub(crate) struct RustGenCtx<'a> {
    ast: &'a Ast,
    signature: &'a Signature,
}

impl<'a> RustGenCtx<'a> {
    pub(crate) fn new(ast: &'a Ast, signature: &'a Signature) -> Self {
        Self { ast, signature }
    }

    pub(crate) fn ast(&self) -> &Ast {
        self.ast
    }

    pub(crate) fn signature(&self) -> &Signature {
        self.signature
    }

    pub(crate) fn type_name(&self, typ: TypeId) -> String {
        match self.signature.type_(typ).kind {
            TypeKind::Plain => self
                .signature
                .iter_type_decls()
                .find_map(|(decl, typ0)| {
                    (typ0 == typ).then(|| self.ast.type_decl(decl).name.clone())
                })
                .expect("type id should have a type declaration"),
            TypeKind::Enum => self
                .signature
                .iter_enum_decls()
                .find_map(|(decl, typ0)| {
                    (typ0 == typ).then(|| self.ast.enum_decl(decl).name.clone())
                })
                .expect("enum type id should have an enum declaration"),
            TypeKind::Model => self
                .signature
                .iter_model_decls()
                .find_map(|(decl, ids)| {
                    (ids.type_ == typ).then(|| self.ast.model_decl(decl).name.clone())
                })
                .expect("model type id should have a model declaration"),
            TypeKind::Mor(model_type) => format!("{}Mor", self.type_name(model_type)),
        }
    }

    pub(crate) fn func_name(&self, func: FuncId) -> String {
        if let Some((decl, _)) = self
            .signature
            .iter_func_decls()
            .find(|(_, func0)| *func0 == func)
        {
            return self.ast.func_decl(decl).name.clone();
        }

        if let Some((decl, _)) = self
            .signature
            .iter_ctor_decls()
            .find(|(_, func0)| *func0 == func)
        {
            return self.ast.ctor_decl(decl).name.clone();
        }

        if let Some((_decl, ids)) = self
            .signature
            .iter_model_decls()
            .find(|(_, ids)| ids.dom == func || ids.cod == func)
        {
            let mor_name = self.type_name(ids.mor).to_case(Snake);
            let suffix = if ids.dom == func { "dom" } else { "cod" };
            return format!("{mor_name}_{suffix}");
        }

        if let Some((member_type, _)) = self
            .signature
            .iter_mor_app_funcs()
            .find(|(_, func0)| *func0 == func)
        {
            return format!("{}_mor_app", self.type_name(member_type).to_case(Snake));
        }

        panic!("function id should be declared or generated by the signature pass")
    }

    pub(crate) fn rel_name(&self, rel: FlatRel) -> String {
        match rel {
            FlatRel::Pred(pred) => self
                .signature
                .iter_pred_decls()
                .find_map(|(decl, pred0)| {
                    (pred0 == pred).then(|| self.ast.pred_decl(decl).name.clone())
                })
                .expect("predicate id should have a predicate declaration"),
            FlatRel::Func(func) => self.func_name(func),
            FlatRel::ModelMember(member_type) => {
                let model_type = self
                    .signature
                    .type_(member_type)
                    .parents
                    .last()
                    .copied()
                    .expect("model member relation requires a member type");
                let model_name = self.type_name(model_type).to_case(Snake);
                let member_name = self.type_name(member_type).to_case(Snake);
                format!("{model_name}_member_{member_name}")
            }
        }
    }

    pub(crate) fn function_can_be_made_defined(&self, func: FuncId) -> bool {
        let codomain = self.signature.func(func).codomain;
        matches!(
            self.signature.type_(codomain).kind,
            TypeKind::Plain | TypeKind::Model | TypeKind::Mor(_)
        ) || self
            .signature
            .iter_ctor_decls()
            .any(|(_, func0)| func0 == func)
    }

    pub(crate) fn pred_like_rels(&self) -> impl Iterator<Item = FlatRel> + '_ {
        self.signature.iter_preds().map(FlatRel::Pred).chain(
            self.signature.iter_types().filter_map(|typ| {
                (!self.signature.type_(typ).parents.is_empty()).then_some(FlatRel::ModelMember(typ))
            }),
        )
    }
}