From 526aac370e1b465dc8c82e6f999d032391f6efa5 Mon Sep 17 00:00:00 2001 From: Kubat <maelle.martin@proton.me> Date: Sat, 3 May 2025 20:43:37 +0200 Subject: [PATCH] [VN] Add the base setters & getters for the graph NOTE: We ensures that block's ids are unique. --- grimoire/src/ast/block.rs | 11 ++-- grimoire/src/error.rs | 3 ++ grimoire/src/graph.rs | 104 +++++++++++++++++++++++++++++++++++--- 3 files changed, 106 insertions(+), 12 deletions(-) diff --git a/grimoire/src/ast/block.rs b/grimoire/src/ast/block.rs index 28bb960..bfbb844 100644 --- a/grimoire/src/ast/block.rs +++ b/grimoire/src/ast/block.rs @@ -14,10 +14,13 @@ impl Default for Block { fn default() -> Self { use std::sync::atomic::{AtomicU64, Ordering}; static INTERNAL_ID: AtomicU64 = AtomicU64::new(1); - Self { - internal_id: INTERNAL_ID.fetch_add(1, Ordering::Relaxed), - name: Default::default(), - spells: Default::default(), + match INTERNAL_ID.fetch_add(1, Ordering::Relaxed) { + u64::MAX => panic!("reached the max number of blocks, how did you did it?"), + internal_id => Self { + internal_id, + name: Default::default(), + spells: Default::default(), + }, } } } diff --git a/grimoire/src/error.rs b/grimoire/src/error.rs index 7750588..b957f9a 100644 --- a/grimoire/src/error.rs +++ b/grimoire/src/error.rs @@ -14,6 +14,9 @@ pub enum Error { #[error("unexpect: {0}")] Unexpected(std::borrow::Cow<'static, str>), + #[error("redefined: {0}")] + Redefined(std::borrow::Cow<'static, str>), + #[error("undefined spell '{0}'")] Undefined(std::borrow::Cow<'static, str>), diff --git a/grimoire/src/graph.rs b/grimoire/src/graph.rs index 87231da..87c7f81 100644 --- a/grimoire/src/graph.rs +++ b/grimoire/src/graph.rs @@ -2,30 +2,89 @@ use crate::{ ast::{Block, BlockId, Expression}, error::Error, }; -use std::collections::HashMap; +use derive_more::Display; +use std::{ + collections::{HashMap, HashSet}, + hash::Hash, +}; + +#[derive(Debug, Display)] +#[display("Edge({_0} -> {_1})")] +pub struct Edge(BlockId, BlockId, Option<Expression>); + +impl Edge { + pub fn new(from: BlockId, to: BlockId) -> Self { + Self(from, to, None) + } + + pub fn with_condition(from: BlockId, to: BlockId, predicate: Expression) -> Self { + Self(from, to, Some(predicate)) + } +} + +impl Eq for Edge {} +impl PartialEq for Edge { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} + +impl Hash for Edge { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.0.hash(state); + self.1.hash(state); + } +} #[derive(Default)] pub struct Graph { nodes: HashMap<BlockId, Block>, - edges: Vec<(BlockId, BlockId, Option<Expression>)>, + edges: HashSet<Edge>, id_mapping: HashMap<String, BlockId>, } impl Graph { - pub fn add_node(&mut self, block: Block) -> Result<BlockId, Error> { - todo!() + /// Note, can't fail becose block's ids are unique! + pub fn add_node(&mut self, block: Block) -> BlockId { + let (id, block) = block.into_parts(); + + if let Some(name) = block.name() { + self.id_mapping.insert(name.to_string(), id); + } + self.nodes.insert(id, block); + + id } pub fn get_node(&self, id: BlockId) -> Result<&Block, Error> { - todo!() + self.nodes + .get(&id) + .ok_or(Error::Undefined(format!("block id {id}").into())) + } + + pub fn get_node_by_name(&self, name: impl AsRef<str>) -> Result<&Block, Error> { + self.get_node(*self.id_mapping.get(name.as_ref()).ok_or(Error::Undefined( + format!("block with name {}", name.as_ref()).into(), + ))?) } pub fn mut_node(&mut self, id: BlockId) -> Result<&mut Block, Error> { - todo!() + self.nodes + .get_mut(&id) + .ok_or(Error::Undefined(format!("block id {id}").into())) + } + + pub fn mut_node_by_name(&mut self, name: impl AsRef<str>) -> Result<&mut Block, Error> { + self.mut_node(*self.id_mapping.get(name.as_ref()).ok_or(Error::Undefined( + format!("block with name {}", name.as_ref()).into(), + ))?) } pub fn add_edge(&mut self, from: BlockId, to: BlockId) -> Result<(), Error> { - todo!() + self.edges + .insert(Edge::new(from, to)) + .then_some(()) + .ok_or_else(|| Error::Redefined(format!("edge from block {from} to block {to}").into())) } pub fn add_conditional_edge( @@ -34,6 +93,35 @@ impl Graph { to: BlockId, predicate: Expression, ) -> Result<(), Error> { - todo!() + self.edges + .insert(Edge::with_condition(from, to, predicate)) + .then_some(()) + .ok_or_else(|| Error::Redefined(format!("edge from block {from} to block {to}").into())) + } + + pub fn successors(&self, from: BlockId) -> Vec<(BlockId, Option<&Expression>)> { + (self.edges.iter()) + .filter_map(|Edge(f, to, pred)| (*f == from).then_some((*to, pred.as_ref()))) + .collect() + } + + pub fn conditional_successors(&self, from: BlockId) -> Vec<(BlockId, &Expression)> { + (self.edges.iter()) + .flat_map(|Edge(f, to, pred)| { + (*f == from) + .then(|| pred.as_ref().map(|pred| (*to, pred))) + .flatten() + }) + .collect() + } + + pub fn unconditional_successor(&self, from: BlockId) -> Result<Option<BlockId>, Error> { + if self.edges.iter().filter(|Edge(f, ..)| *f == from).count() <= 1 { + return Err(Error::Unexpected( + "got multiple unconditional successors for a single block".into(), + )); + } + + Ok((self.edges.iter()).find_map(|Edge(f, to, _)| (*f == from).then_some(*to))) } } -- GitLab