celox-slt 0.4.1

Source-independent symbolic logic tree core for Celox
Documentation
use std::{fmt, hash::Hash};

use serde::{Deserialize, Serialize};

use celox_design::VarAtomBase;

use crate::{HashMap, HashSet, NodeId, SLTNodeArena, SLTNodeFactsError};

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct LogicPathId(pub usize);

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(
    serialize = "A: Serialize + std::hash::Hash + Eq",
    deserialize = "A: Deserialize<'de> + std::hash::Hash + Eq + Clone"
))]
pub enum LogicPathTarget<A: Hash + Eq + Clone> {
    Var(VarAtomBase<A>),
    CombCaptureEvent {
        site_id: u32,
        guard: Option<NodeId>,
        emit_on_true: bool,
        args: Vec<NodeId>,
        loop_runner: Option<NodeId>,
        fatal_error_code: Option<i64>,
        consume_enabled: bool,
    },
}

impl<A: Hash + Eq + Clone> LogicPathTarget<A> {
    pub fn var(&self) -> Option<&VarAtomBase<A>> {
        match self {
            LogicPathTarget::Var(var) => Some(var),
            LogicPathTarget::CombCaptureEvent { .. } => None,
        }
    }
}

impl<A: fmt::Display + Hash + Eq + Clone> fmt::Display for LogicPathTarget<A> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LogicPathTarget::Var(var) => write!(f, "{var}"),
            LogicPathTarget::CombCaptureEvent { site_id, .. } => {
                write!(f, "capture_event({site_id})")
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(
    serialize = "A: Serialize + std::hash::Hash + Eq",
    deserialize = "A: Deserialize<'de> + std::hash::Hash + Eq + Clone"
))]
pub struct LogicPath<A: Hash + Eq + Clone> {
    /// Semantic combinational process which produced this range definition.
    ///
    pub target: LogicPathTarget<A>,
    pub sources: HashSet<VarAtomBase<A>>,
    pub previous_sources: HashSet<VarAtomBase<A>>,
    /// Sources used to compute a dynamic address.  These remain ordinary
    /// dependencies even when they overlap a previous-value source.
    #[serde(default)]
    pub address_sources: HashSet<VarAtomBase<A>>,
    pub local_inputs: Vec<(A, NodeId)>,
    pub order_before: HashSet<LogicPathId>,
    pub comb_capture_enable_sites: Vec<u32>,
    /// Enable the listed capture sites whenever this path executes, even when
    /// assignment conversion leaves the stored target unchanged.
    #[serde(default)]
    pub comb_capture_enable_always: bool,
    pub pre_lower_nodes: Vec<NodeId>,
    pub expr: NodeId,
}

impl<A: fmt::Display + Hash + Eq + Clone> fmt::Display for LogicPath<A> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.target)
    }
}

impl<A: fmt::Debug + fmt::Display + Hash + Eq + Clone> LogicPath<A> {
    pub fn map_addr<B: Hash + Eq + Clone, F>(
        &self,
        arena: &SLTNodeArena<A>,
        target_arena: &mut SLTNodeArena<B>,
        cache: &mut HashMap<NodeId, NodeId>,
        f: &F,
    ) -> Result<LogicPath<B>, SLTNodeFactsError>
    where
        F: Fn(&A) -> B,
    {
        let target = match &self.target {
            LogicPathTarget::Var(var) => {
                LogicPathTarget::Var(VarAtomBase::new(f(&var.id), var.access.lsb, var.access.msb))
            }
            LogicPathTarget::CombCaptureEvent {
                site_id,
                guard,
                emit_on_true,
                args,
                loop_runner,
                fatal_error_code,
                consume_enabled,
            } => LogicPathTarget::CombCaptureEvent {
                site_id: *site_id,
                guard: guard
                    .map(|node| {
                        arena
                            .get(node)
                            .map_addr(node, arena, target_arena, cache, f)
                    })
                    .transpose()?,
                emit_on_true: *emit_on_true,
                args: args
                    .iter()
                    .map(|node| {
                        arena
                            .get(*node)
                            .map_addr(*node, arena, target_arena, cache, f)
                    })
                    .collect::<Result<Vec<_>, SLTNodeFactsError>>()?,
                loop_runner: loop_runner
                    .map(|node| {
                        arena
                            .get(node)
                            .map_addr(node, arena, target_arena, cache, f)
                    })
                    .transpose()?,
                fatal_error_code: *fatal_error_code,
                consume_enabled: *consume_enabled,
            },
        };
        let local_inputs = self
            .local_inputs
            .iter()
            .map(|(id, node)| {
                Ok((
                    f(id),
                    arena
                        .get(*node)
                        .map_addr(*node, arena, target_arena, cache, f)?,
                ))
            })
            .collect::<Result<Vec<_>, SLTNodeFactsError>>()?;
        let pre_lower_nodes = self
            .pre_lower_nodes
            .iter()
            .map(|node| {
                arena
                    .get(*node)
                    .map_addr(*node, arena, target_arena, cache, f)
            })
            .collect::<Result<Vec<_>, SLTNodeFactsError>>()?;
        let expr = arena
            .get(self.expr)
            .map_addr(self.expr, arena, target_arena, cache, f)?;

        Ok(LogicPath {
            target,
            sources: self
                .sources
                .iter()
                .map(|v| VarAtomBase::new(f(&v.id), v.access.lsb, v.access.msb))
                .collect(),
            previous_sources: self
                .previous_sources
                .iter()
                .map(|v| VarAtomBase::new(f(&v.id), v.access.lsb, v.access.msb))
                .collect(),
            address_sources: self
                .address_sources
                .iter()
                .map(|v| VarAtomBase::new(f(&v.id), v.access.lsb, v.access.msb))
                .collect(),
            local_inputs,
            order_before: self.order_before.clone(),
            comb_capture_enable_sites: self.comb_capture_enable_sites.clone(),
            comb_capture_enable_always: self.comb_capture_enable_always,
            pre_lower_nodes,
            expr,
        })
    }
}