Skip to content

Commit

Permalink
Edge-case tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
wrsturgeon committed Nov 4, 2023
1 parent bddcc99 commit 75707f2
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 250 deletions.
6 changes: 3 additions & 3 deletions automata/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ impl<S: Stack> Action<S> {
#[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(),
}
}
}
2 changes: 1 addition & 1 deletion automata/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> fmt::Display for IllFormed<I, S, C> {
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(),
)
Expand Down
2 changes: 1 addition & 1 deletion automata/src/combinators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl<I: Input, S: Stack> ops::Shr for Deterministic<I, S> {
let accepting_tags: BTreeSet<String> = self
.tags
.iter()
.filter(|&(_, v)| v.iter().any(|i| accepting_indices.contains(i)))
.filter(|&(_, v)| accepting_indices.contains(v))
.map(|(k, _)| k.clone())
.collect();

Expand Down
134 changes: 83 additions & 51 deletions automata/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct Graph<I: Input, S: Stack, C: Ctrl<I, S>> {
/// Initial state of the machine (before reading input).
pub initial: C,
/// Map string tags to the state or states they represent.
pub tags: BTreeMap<String, BTreeSet<usize>>,
pub tags: BTreeMap<String, usize>,
}

impl<I: Input, S: Stack, C: Ctrl<I, S>> Clone for Graph<I, S, C> {
Expand Down Expand Up @@ -70,38 +70,29 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
pub fn check(&self) -> Result<(), IllFormed<I, S, C>> {
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));
}
}
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()));
}
}
}
Expand All @@ -112,11 +103,9 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
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| {
Expand All @@ -140,17 +129,17 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
}
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);
}
Expand All @@ -174,6 +163,9 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
// 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<C> = subsets_as_states.keys().cloned().collect();
Expand All @@ -195,7 +187,54 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
}
})
.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
Expand All @@ -217,15 +256,11 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
};

// 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<I, S, C> = match try_merge(result_iterator) {
Expand Down Expand Up @@ -289,7 +324,7 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {

/// 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.
Expand All @@ -301,11 +336,8 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
.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
Expand Down
24 changes: 9 additions & 15 deletions automata/src/in_progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,15 @@ fn step<I: Input, S: Stack, C: Ctrl<I, S>>(
.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))
Expand All @@ -133,12 +130,9 @@ fn step<I: Input, S: Stack, C: Ctrl<I, S>>(
};
};
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(
Expand Down
6 changes: 3 additions & 3 deletions automata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}};
Expand All @@ -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()
}
Expand All @@ -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
}};
Expand Down
8 changes: 2 additions & 6 deletions automata/src/map_indices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Graph<I, S, C> {
.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(),
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions automata/src/qc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ impl<S: Arbitrary + Stack, C: Arbitrary + Ctrl<u8, S>> Arbitrary for Graph<u8, S
continue 'sort_again;
}
}
let mut tags = BTreeMap::<String, BTreeSet<usize>>::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,
Expand Down
2 changes: 1 addition & 1 deletion automata/src/reindex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl<I: Input, S: Stack, C: Ctrl<I, S>> Transition<I, S, C> {
/// 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<I, S, C>],
Expand Down
27 changes: 5 additions & 22 deletions src/fixpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,25 @@

//! 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.
#[must_use = "Fixpoints do nothing unless applied to an automaton with the `>>` operator."]
#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Fixpoint(String);

impl<I: Input, S: Stack, C: Ctrl<I, S>> ops::Shr<Graph<I, S, C>> for Fixpoint {
type Output = Graph<I, S, C>;
impl<I: Input, S: Stack> ops::Shr<Deterministic<I, S>> for Fixpoint {
type Output = Deterministic<I, S>;
#[inline]
#[allow(clippy::manual_assert, clippy::panic)]
fn shr(self, rhs: Graph<I, S, C>) -> Self::Output {
fn shr(self, rhs: Deterministic<I, S>) -> 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 {
Expand Down
Loading

0 comments on commit 75707f2

Please sign in to comment.