diff --git a/automata/src/action.rs b/automata/src/action.rs index 9052643..5656024 100644 --- a/automata/src/action.rs +++ b/automata/src/action.rs @@ -75,9 +75,9 @@ impl Action { #[inline] pub fn in_english(&self) -> String { match *self { - Self::Local => "do nothing to".to_owned(), - Self::Push(ref s) => format!("push `{}` onto", s.to_src()), - Self::Pop => "pop from".to_owned(), + Self::Local => "leave the stack untouched".to_owned(), + Self::Push(ref s) => format!("push `{}` onto the stack", s.to_src()), + Self::Pop => "pop from the stack".to_owned(), } } } diff --git a/automata/src/check.rs b/automata/src/check.rs index 4a4decf..c1e46aa 100644 --- a/automata/src/check.rs +++ b/automata/src/check.rs @@ -145,7 +145,7 @@ impl> fmt::Display for IllFormed { Self::IncompatibleStackActions(ref a, ref b) => { write!( f, - "Can't {} and {} the stack at the same time.", + "Can't {} and {} at the same time.", a.in_english(), b.in_english(), ) diff --git a/automata/src/combinators.rs b/automata/src/combinators.rs index 8909fbd..971d985 100644 --- a/automata/src/combinators.rs +++ b/automata/src/combinators.rs @@ -70,7 +70,7 @@ impl ops::Shr for Deterministic { let accepting_tags: BTreeSet = self .tags .iter() - .filter(|&(_, v)| v.iter().any(|i| accepting_indices.contains(i))) + .filter(|&(_, v)| accepting_indices.contains(v)) .map(|(k, _)| k.clone()) .collect(); diff --git a/automata/src/graph.rs b/automata/src/graph.rs index fc1d702..6935242 100644 --- a/automata/src/graph.rs +++ b/automata/src/graph.rs @@ -37,7 +37,7 @@ pub struct Graph> { /// Initial state of the machine (before reading input). pub initial: C, /// Map string tags to the state or states they represent. - pub tags: BTreeMap>, + pub tags: BTreeMap, } impl> Clone for Graph { @@ -70,10 +70,10 @@ impl> Graph { pub fn check(&self) -> Result<(), IllFormed> { let n_states = self.states.len(); for r in self.initial.view() { - let states: BTreeSet<&_> = match r { + let state = match r { Ok(i) => { if let Some(state) = self.states.get(i) { - iter::once(state).collect() + state } else { return Err(IllFormed::OutOfBounds(i)); } @@ -81,27 +81,18 @@ impl> Graph { Err(tag) => self .tags .get(tag) - .map(|set| set.iter().map(|&i| get!(self.states, i)).collect()) + .map(|&i| get!(self.states, i)) .ok_or(IllFormed::TagDNE(tag.to_owned()))?, }; - let typed = states.into_iter().try_fold(vec![], |mut acc, s| { - acc.push((s, s.input_type()?)); - Ok(acc) - })?; - for (state, input_t) in typed { - #[allow(clippy::match_same_arms)] // TBD - match input_t { - None => {} - Some(t) if t == "()" => {} - Some(other) => return Err(IllFormed::InitialNotUnit(other)), + if let Some(t) = state.input_type()? { + if t != "()" { + return Err(IllFormed::InitialNotUnit(t)); } - for curry in state.transitions.values() { - for transition in curry.values() { - if transition.update.input_t != "()" { - return Err(IllFormed::InitialNotUnit( - transition.update.input_t.clone(), - )); - } + } + for curry in state.transitions.values() { + for transition in curry.values() { + if transition.update.input_t != "()" { + return Err(IllFormed::InitialNotUnit(transition.update.input_t.clone())); } } } @@ -112,11 +103,9 @@ impl> Graph { return Err(IllFormed::DuplicateState(Box::new(state.clone()))); } } - for pointers in self.tags.values() { - for &index in pointers { - if index >= n_states { - return Err(IllFormed::OutOfBounds(index)); - } + for &index in self.tags.values() { + if index >= n_states { + return Err(IllFormed::OutOfBounds(index)); } } NonZeroUsize::new(n_states).map_or(Ok(()), |nz| { @@ -140,17 +129,17 @@ impl> Graph { } for r in run.ctrl.view() { if match r { - Ok(i) => vec![get!(self.states, i)], - Err(tag) => self - .tags - .get(tag) - .ok_or(ParseError::BadParser(IllFormed::TagDNE(tag.to_owned())))? - .iter() - .map(|&i| get!(self.states, i)) - .collect(), + Ok(i) => get!(self.states, i), + Err(tag) => get!( + self.states, + *self + .tags + .get(tag) + .ok_or(ParseError::BadParser(IllFormed::TagDNE(tag.to_owned())))? + ), } - .into_iter() - .any(|s| s.non_accepting.is_empty()) + .non_accepting + .is_empty() { return Ok(run.output_t); } @@ -174,6 +163,9 @@ impl> Graph { // Associate each subset of states with a merged state let mut subsets_as_states = BTreeMap::new(); self.explore(&mut subsets_as_states, &self.initial)?; + for &i in self.tags.values() { + self.explore(&mut subsets_as_states, &C::from_usize(i))?; + } // Fix an ordering on those subsets let mut ordering: Vec = subsets_as_states.keys().cloned().collect(); @@ -195,7 +187,54 @@ impl> Graph { } }) .collect(), - tags: BTreeMap::new(), + tags: self + .tags + .iter() + .map(|(k, &v)| { + ( + k.clone(), + unwrap!(ordering.binary_search(&C::from_usize(v))), + // unwrap!(ordering.binary_search_by(|c| { + // // Make sure there's only one element and name it `r` + // let r = { + // let mut view = c.view(); + // let Some(r) = view.next() else { + // return Ordering::Less; + // }; + // if view.next().is_some() { + // return Ordering::Greater; + // } + // r + // }; + // match r { + // Ok(ref i) => i, + // Err(tag) => unwrap!(self.tags.get(tag)), + // } + // .cmp(&v) + // })), + // ordering + // .iter() + // .position(|c| { + // // Make sure there's only one element and name it `r` + // let r = { + // let mut view = c.view(); + // let Some(r) = view.next() else { + // return false; + // }; + // if view.next().is_some() { + // return false; + // } + // r + // }; + // match r { + // Ok(i) => i == v, + // Err(tag) => self.tags.get(tag) == Some(&v), + // } + // }) + // .unwrap(), + ) + }) + .collect(), }; output.sort(); output @@ -217,15 +256,11 @@ impl> Graph { }; // Merge this subset of states into one (most of the heavy lifting) - let result_iterator = subset.view().flat_map(|r| match r { - Ok(i) => vec![Ok(get!(self.states, i).clone())], + let result_iterator = subset.view().map(|r| match r { + Ok(i) => Ok(get!(self.states, i).clone()), Err(s) => self.tags.get(s).map_or_else( - || vec![Err(IllFormed::TagDNE(s.to_owned()))], - |set| { - set.iter() - .map(|&i| Ok(get!(self.states, i).clone())) - .collect() - }, + || Err(IllFormed::TagDNE(s.to_owned())), + |&i| Ok(get!(self.states, i).clone()), ), }); let mega_state: State = match try_merge(result_iterator) { @@ -289,7 +324,7 @@ impl> Graph { /// Change nothing about the semantics but sort the internal vector of states. #[inline] - #[allow(clippy::missing_panics_doc, unused_unsafe)] + #[allow(clippy::missing_panics_doc)] pub fn sort(&mut self) { // Associate each original index with a concrete state instead of just an index, // since we're going to be swapping the indices around. @@ -301,11 +336,8 @@ impl> Graph { .initial .clone() .map_indices(|i| unwrap!(self.states.binary_search(unwrap!(index_map.get(&i))))); - for tags in self.tags.values_mut() { - *tags = tags - .iter() - .map(|i| unwrap!(self.states.binary_search(unwrap!(index_map.get(i))))) - .collect(); + for i in self.tags.values_mut() { + *i = unwrap!(self.states.binary_search(unwrap!(index_map.get(i)))); } // Can't do this in-place since the entire state array is required as an argument. self.states = self diff --git a/automata/src/in_progress.rs b/automata/src/in_progress.rs index a588128..4f64bec 100644 --- a/automata/src/in_progress.rs +++ b/automata/src/in_progress.rs @@ -112,18 +112,15 @@ fn step>( .ok_or(ParseError::BadParser(IllFormed::TagDNE(tag.to_owned()))), })?; let mut states = ctrl.view().map(|r| match r { - Ok(i) => vec![get!(graph.states, i)], - Err(tag) => graph - .tags - .get(tag) - .unwrap_or_else(|| never!()) - .iter() - .map(|&i| get!(graph.states, i)) - .collect(), + Ok(i) => get!(graph.states, i), + Err(tag) => get!( + graph.states, + *graph.tags.get(tag).unwrap_or_else(|| never!()) + ), }); let Some(token) = maybe_token else { return if stack.is_empty() { - if states.any(|set| set.into_iter().any(|s| s.non_accepting.is_empty())) { + if states.any(|s| s.non_accepting.is_empty()) { Ok((None, output_t.to_owned())) } else { Err(ParseError::BadInput(InputError::NotAccepting)) @@ -133,12 +130,9 @@ fn step>( }; }; let maybe_stack_top = stack.last(); - let transitions = states.flat_map(|set| { - set.into_iter() - .filter_map(|s| match s.transitions.get(maybe_stack_top, &token) { - Err(e) => Some(Err(e)), - Ok(opt) => opt.map(Ok), - }) + let transitions = states.filter_map(|s| match s.transitions.get(maybe_stack_top, &token) { + Err(e) => Some(Err(e)), + Ok(opt) => opt.map(Ok), }); try_merge(transitions).map_or(Err(ParseError::BadInput(InputError::Absurd)), |r| { r.map_or_else( diff --git a/automata/src/lib.rs b/automata/src/lib.rs index 11b4488..d067b16 100644 --- a/automata/src/lib.rs +++ b/automata/src/lib.rs @@ -109,7 +109,7 @@ macro_rules! unwrap { #[cfg(not(any(debug_assertions, test)))] macro_rules! unwrap { ($expr:expr) => {{ - #[allow(unsafe_code)] + #[allow(unsafe_code, unused_unsafe)] let result = unsafe { $expr.unwrap_unchecked() }; result }}; @@ -127,7 +127,7 @@ macro_rules! never { #[cfg(not(any(debug_assertions, test)))] macro_rules! never { () => {{ - #[allow(unsafe_code)] + #[allow(unsafe_code, unused_unsafe)] unsafe { core::hint::unreachable_unchecked() } @@ -146,7 +146,7 @@ macro_rules! get { #[cfg(not(any(debug_assertions, test)))] macro_rules! get { ($expr:expr, $index:expr) => {{ - #[allow(unsafe_code)] + #[allow(unsafe_code, unused_unsafe)] let result = unsafe { $expr.get_unchecked($index) }; result }}; diff --git a/automata/src/map_indices.rs b/automata/src/map_indices.rs index d525081..2039cfd 100644 --- a/automata/src/map_indices.rs +++ b/automata/src/map_indices.rs @@ -19,12 +19,8 @@ impl> Graph { .into_iter() .map(|s| s.map_indices(&mut f)) .collect(), - tags: self - .tags - .into_iter() - .map(|(k, v)| (k, v.into_iter().map(&mut f).collect())) - .collect(), - initial: self.initial.map_indices(f), + initial: self.initial.map_indices(&mut f), + tags: self.tags.into_iter().map(|(k, v)| (k, f(v))).collect(), } } } diff --git a/automata/src/qc.rs b/automata/src/qc.rs index 378b441..91ffbd0 100644 --- a/automata/src/qc.rs +++ b/automata/src/qc.rs @@ -72,9 +72,9 @@ impl> Arbitrary for Graph>::arbitrary(g); - for set in tags.values_mut() { - *set = set.iter().map(|&i| i % nz_post).collect(); + let mut tags = BTreeMap::arbitrary(g); + for i in tags.values_mut() { + *i = *i % nz; } return Self { states, diff --git a/automata/src/reindex.rs b/automata/src/reindex.rs index a12bfe7..fb29bf6 100644 --- a/automata/src/reindex.rs +++ b/automata/src/reindex.rs @@ -87,7 +87,7 @@ impl> Transition { /// Update index "pointers" in response to a reordered array. #[inline] #[must_use] - #[allow(clippy::missing_panics_doc, unused_unsafe)] + #[allow(clippy::missing_panics_doc)] pub fn reindex( &self, states: &[State], diff --git a/src/fixpoint.rs b/src/fixpoint.rs index e963db7..f01b480 100644 --- a/src/fixpoint.rs +++ b/src/fixpoint.rs @@ -6,7 +6,7 @@ //! Fixpoint: just a tagged state that can be called later. -use core::{iter, ops}; +use core::ops; use inator_automata::*; /// Tagged state that can be called later. @@ -14,34 +14,17 @@ use inator_automata::*; #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Fixpoint(String); -impl> ops::Shr> for Fixpoint { - type Output = Graph; +impl ops::Shr> for Fixpoint { + type Output = Deterministic; #[inline] #[allow(clippy::manual_assert, clippy::panic)] - fn shr(self, rhs: Graph) -> Self::Output { + fn shr(self, rhs: Deterministic) -> Self::Output { let Graph { states, initial, mut tags, } = rhs; - let init_set = initial - .view() - .flat_map(|r| { - r.map_or_else( - |tag| { - tags.get(tag) - .expect( - "Weird error: \ - an earlier parser called a fixpoint by name, \ - but that name was nowhere to be found.", - ) - .clone() - }, - |i| iter::once(i).collect(), - ) - }) - .collect(); - if tags.insert(self.0, init_set).is_some() { + if tags.insert(self.0, initial).is_some() { panic!("Fixpoint name already in use"); } let mut out = Graph { diff --git a/src/lib.rs b/src/lib.rs index 68e73e5..ce6d059 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ macro_rules! unwrap { #[cfg(not(any(debug_assertions, test)))] macro_rules! unwrap { ($expr:expr) => {{ - #[allow(unsafe_code)] + #[allow(unsafe_code, unused_unsafe)] let result = unsafe { $expr.unwrap_unchecked() }; result }}; @@ -130,7 +130,7 @@ macro_rules! get { #[cfg(not(any(debug_assertions, test)))] macro_rules! get { ($expr:expr, $index:expr) => {{ - #[allow(unsafe_code)] + #[allow(unsafe_code, unused_unsafe)] let result = unsafe { $expr.get_unchecked($index) }; result }}; diff --git a/src/recurse.rs b/src/recurse.rs index 5c6c093..af28d8d 100644 --- a/src/recurse.rs +++ b/src/recurse.rs @@ -46,7 +46,7 @@ impl> ops::Shr for Graph { let accepting_tags = self .tags .iter() - .filter(|&(_, v)| v.iter().any(|i| accepting_indices.contains(i))) + .filter(|&(_, i)| accepting_indices.contains(i)) .map(|(k, _)| k.clone()) .collect(); let mut out = Graph { diff --git a/src/test.rs b/src/test.rs index 80a90cb..4122112 100644 --- a/src/test.rs +++ b/src/test.rs @@ -43,27 +43,26 @@ mod prop { parser.accept(input.iter().copied()).is_ok() == (input.len() == 1 && range.contains(&input[0])) } - fn fixpoint_unused(parser: Nondeterministic, input: Vec) -> bool { + fn fixpoint_unused(parser: Deterministic, input: Vec) -> bool { let pre = parser.accept(input.iter().copied()); let post = (fixpoint("unused") >> parser).accept(input); pre == post } - fn fixpoint_repeat(parser: Nondeterministic, both: Vec) -> bool { + fn fixpoint_repeat(parser: Deterministic, both: Vec) -> bool { if parser.check().is_err() { - return false; + return true; // not our problem } if parser.accept(iter::empty()).is_err() { return true; } let sliceable = sliceable(&parser, &both); - let repeated = fixpoint("da capo") >> parser >> recurse("da capo"); + let Ok(repeated) = panic::catch_unwind(|| fixpoint("da capo") >> parser >> recurse("da capo")) else { + return true; + }; if repeated.check().is_err() { return false; } - if repeated.determinize().is_err() { - return true; - } let output = repeated.accept(both); if matches!(output, Err(ParseError::BadParser(_))) { return true; @@ -71,6 +70,7 @@ mod prop { output.is_ok() == sliceable } + /* fn fixpoint_repeat_twice(lhs: Deterministic, rhs: Deterministic, both: Vec) -> bool { if lhs.accept(iter::empty()).is_err() || rhs.accept(iter::empty()).is_err() { return true; @@ -85,15 +85,13 @@ mod prop { if repeated.check().is_err() { return false; } - if repeated.determinize().is_err() { - return true; - } let output = repeated.accept(both); if matches!(output, Err(ParseError::BadParser(_))) { return true; } output.is_ok() == sliceable } + */ } } @@ -103,18 +101,19 @@ mod reduced { use super::*; use std::panic; - fn fixpoint_repeat(parser: Nondeterministic, both: Vec) { + fn fixpoint_repeat(parser: Deterministic, both: Vec) { parser.check().unwrap(); if parser.accept(iter::empty()).is_err() { return; } let sliceable = sliceable(&parser, &both); - let repeated = fixpoint("da capo") >> parser >> recurse("da capo"); + let Ok(repeated) = + panic::catch_unwind(|| fixpoint("da capo") >> parser >> recurse("da capo")) + else { + return; + }; println!("Repeated: {repeated:#?}"); repeated.check().unwrap(); - if repeated.determinize().is_err() { - return; - } let mut run = both.iter().copied().run(&repeated); println!(" {run:?}"); while let Some(r) = run.next() { @@ -127,6 +126,7 @@ mod reduced { assert_eq!(output.is_ok(), sliceable, "{output:?}"); } + /* fn fixpoint_repeat_twice( lhs: Deterministic, rhs: Deterministic, @@ -146,9 +146,6 @@ mod reduced { let repeated = fixpoint("da capo") >> lhs >> rhs >> recurse("da capo"); println!("Repeated: {repeated:#?}"); repeated.check().unwrap(); - if repeated.determinize().is_err() { - return; - } let mut run = both.iter().copied().run(&repeated); println!(" {run:?}"); while let Some(r) = run.next() { @@ -160,11 +157,12 @@ mod reduced { } assert_eq!(output.is_ok(), sliceable, "{output:?}"); } + */ #[test] fn fixpoint_repeat_1() { fixpoint_repeat( - Graph { + Nondeterministic { states: vec![ State { transitions: CurryStack { @@ -189,7 +187,9 @@ mod reduced { ], initial: [Ok(0), Ok(1)].into_iter().collect(), tags: BTreeMap::new(), - }, + } + .determinize() + .unwrap(), vec![0], ); } @@ -210,7 +210,7 @@ mod reduced { State { transitions: CurryStack { wildcard: Some(CurryInput::Wildcard(Transition { - dst: iter::once(Ok(0)).collect(), + dst: 0, act: Action::Local, update: update!(|(), _| {}), })), @@ -220,7 +220,7 @@ mod reduced { non_accepting: BTreeSet::new(), }, ], - initial: iter::once(Ok(1)).collect(), + initial: 1, tags: BTreeMap::new(), }, vec![0, 0, 0], @@ -239,7 +239,7 @@ mod reduced { }, non_accepting: BTreeSet::new(), }], - initial: iter::once(Ok(0)).collect(), + initial: 0, tags: BTreeMap::new(), }, vec![0], @@ -249,7 +249,7 @@ mod reduced { #[test] fn fixpoint_repeat_4() { fixpoint_repeat( - Graph { + Nondeterministic { states: vec![ State { transitions: CurryStack { @@ -286,24 +286,22 @@ mod reduced { ], initial: iter::once(Ok(0)).collect(), tags: BTreeMap::new(), - }, + } + .determinize() + .unwrap(), vec![], ); } #[test] - fn fixpoint_repeat_5() { + fn fixpoint_repeat_6() { fixpoint_repeat( Graph { states: vec![ State { transitions: CurryStack { wildcard: None, - map_none: Some(CurryInput::Wildcard(Transition { - dst: iter::once(Ok(2)).collect(), - act: Action::Local, - update: update!(|(), _| {}), - })), + map_none: None, map_some: BTreeMap::new(), }, non_accepting: BTreeSet::new(), @@ -311,150 +309,50 @@ mod reduced { State { transitions: CurryStack { wildcard: None, - map_none: Some(CurryInput::Scrutinize(RangeMap { - entries: BTreeMap::new(), - })), - map_some: BTreeMap::new(), - }, - non_accepting: BTreeSet::new(), - }, - State { - transitions: CurryStack { - wildcard: Some(CurryInput::Wildcard(Transition { - dst: iter::once(Ok(0)).collect(), - act: Action::Pop, - update: update!(|(), _| {}), - })), map_none: None, map_some: BTreeMap::new(), }, - non_accepting: BTreeSet::new(), + non_accepting: iter::once(String::new()).collect(), }, ], - initial: iter::once(Ok(0)).collect(), - tags: BTreeMap::new(), + initial: 0, + tags: iter::once((String::new(), 1)).collect(), }, - vec![0, 0], + vec![], ); } #[test] - fn fixpoint_repeat_6() { + fn fixpoint_repeat_7() { fixpoint_repeat( Graph { states: vec![ - State { - transitions: CurryStack { - wildcard: None, - map_none: None, - map_some: iter::once(( - 255, - CurryInput::Wildcard(Transition { - dst: iter::once(Ok(0)).collect(), - act: Action::Pop, - update: update!(|(), _| {}), - }), - )) - .collect(), - }, - non_accepting: BTreeSet::new(), - }, State { transitions: CurryStack { wildcard: None, map_none: Some(CurryInput::Wildcard(Transition { - dst: iter::once(Ok(1)).collect(), - act: Action::Push(255), + dst: 0, + act: Action::Local, update: update!(|(), _| {}), })), map_some: BTreeMap::new(), }, non_accepting: BTreeSet::new(), }, - ], - initial: [Ok(0), Ok(1)].into_iter().collect(), - tags: BTreeMap::new(), - }, - vec![0, 0], - ); - } - - #[test] - fn fixpoint_repeat_7() { - fixpoint_repeat( - Graph { - states: vec![ State { transitions: CurryStack { wildcard: None, map_none: Some(CurryInput::Wildcard(Transition { - dst: iter::once(Ok(0)).collect(), - act: Action::Push(0), + dst: 0, + act: Action::Pop, update: update!(|(), _| {}), })), map_some: BTreeMap::new(), }, non_accepting: BTreeSet::new(), }, - State { - transitions: CurryStack { - wildcard: Some(CurryInput::Scrutinize(RangeMap { - entries: iter::once(( - Range { first: 0, last: 0 }, - Transition { - dst: iter::once(Ok(0)).collect(), - act: Action::Pop, - update: update!(|(), _| {}), - }, - )) - .collect(), - })), - map_none: None, - map_some: BTreeMap::new(), - }, - non_accepting: BTreeSet::new(), - }, ], - initial: [Ok(0), Ok(1)].into_iter().collect(), - tags: BTreeMap::new(), - }, - vec![1, 0], - ); - } - - #[test] - fn fixpoint_repeat_twice_1() { - fixpoint_repeat_twice( - Graph { - states: vec![State { - transitions: CurryStack { - wildcard: None, - map_none: None, - map_some: BTreeMap::new(), - }, - non_accepting: BTreeSet::new(), - }], - initial: 0, - tags: BTreeMap::new(), - }, - Graph { - states: vec![State { - transitions: CurryStack { - wildcard: None, - map_none: None, - map_some: iter::once(( - 0, - CurryInput::Wildcard(Transition { - dst: 0, - act: Action::Local, - update: update!(|(), _| {}), - }), - )) - .collect(), - }, - non_accepting: BTreeSet::new(), - }], - initial: 0, + initial: 1, tags: BTreeMap::new(), }, vec![],