diff --git a/automata/src/graph.rs b/automata/src/graph.rs index 1988c25..ca52ab6 100644 --- a/automata/src/graph.rs +++ b/automata/src/graph.rs @@ -7,8 +7,8 @@ //! Automaton loosely based on visibly pushdown automata. use crate::{ - try_merge, Check, Ctrl, CurryInput, CurryStack, IllFormed, Input, InputError, ParseError, - RangeMap, Stack, State, ToSrc, Transition, + try_merge, Check, Ctrl, CurryInput, CurryStack, IllFormed, Input, InputError, Merge, + ParseError, RangeMap, Stack, State, ToSrc, Transition, }; use core::{iter, num::NonZeroUsize}; use std::{ @@ -322,6 +322,61 @@ impl> Graph { }) } + /// Compute the input type of any successful run. + /// # Errors + /// If multiple accepting states attempt to return different types. + #[inline] + #[allow(clippy::missing_panics_doc)] + pub fn input_type(&self) -> Result, IllFormed> { + self.initial + .view() + .map(|r| { + r.map_or_else( + |tag| get!(self.states, *unwrap!(self.tags.get(tag))), + |i| get!(self.states, i), + ) + }) + .try_fold(None, |acc, state| { + let shit = + acc.merge(state.transitions.values().try_fold(None, |accc, curry| { + accc.merge({ + curry.values().try_fold(None, |acccc, t| { + acccc.merge(Some(t.update.input_t.clone())).map_or_else( + |(a, b)| { + if a == b { + Ok(Some(a)) + } else { + Err(IllFormed::TypeMismatch(a, b)) + } + }, + Ok, + ) + })? + }) + .map_or_else( + |(a, b)| { + if a == b { + Ok(Some(a)) + } else { + Err(IllFormed::TypeMismatch(a, b)) + } + }, + Ok, + ) + })?); + shit.map_or_else( + |(a, b)| { + if a == b { + Ok(Some(a)) + } else { + Err(IllFormed::TypeMismatch(a, b)) + } + }, + Ok, + ) + }) + } + /// Change nothing about the semantics but sort the internal vector of states. #[inline] #[allow(clippy::panic)] // <-- TODO