Skip to content

Commit

Permalink
hard part still to do
Browse files Browse the repository at this point in the history
  • Loading branch information
wrsturgeon committed Nov 14, 2023
1 parent e2076f1 commit bccdc5b
Showing 1 changed file with 159 additions and 0 deletions.
159 changes: 159 additions & 0 deletions automata/src/combinators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,76 @@ impl<I: Input> ops::Shr<Self> for Deterministic<I> {
}
}

impl<I: Input> ops::BitXor<Self> for Deterministic<I> {
type Output = Self;
#[inline]
fn bitxor(mut self, other: Self) -> Self::Output {
let rhs_init = get!(other.states, other.initial)
.transitions
.clone()
.generalize();

let accepting_indices =
self.states
.iter_mut()
.enumerate()
.fold(BTreeSet::new(), |mut acc_i, (i, st)| {
if st.non_accepting.is_empty() {
st.non_accepting = iter::once(
"Ran the first part of a two-parser call \
(with `^`) but not the second one."
.to_owned(),
)
.collect(); // <-- No longer accepting since we need to run the second parser
let _ = acc_i.insert(i);
}
acc_i
});

let mut s = self.generalize();
if s.check().is_err() {
panic!("Internal error")
}
let size = s.states.len();

let Graph {
states: other_states,
initial: other_initial,
} = other
.generalize()
.map_indices(|i| i.checked_add(size).expect("Absurdly huge number of states"));

s.states.extend(other_states);

// For every transition that an empty stack can take from the initial state of the right-hand parser,
// add that transition (only on the empty stack) to each accepting state of the left-hand parser.
for state in &mut s.states {
state.transitions = mem::replace(
&mut state.transitions,
Curry::Wildcard(Transition::Return { region: "" }),
)
.merge(rhs_init.clone())
.unwrap_or_else(|e| panic!("{e}"));
}

// If any initial states are immediately accepting, we need to start in the second parser, too.
if s.initial.iter().any(|i| accepting_indices.contains(i)) {
s.initial.extend(other_initial.iter().copied());
}

let mut out = Graph {
states: s
.states
.into_iter()
.map(|st| add_call_state(st, &other_initial, &accepting_indices))
.collect(),
..s
};
out.sort();
out.determinize().unwrap_or_else(|e| panic!("{e}"))
}
}

/// Add a tail call to any accepting state.
#[inline]
#[must_use]
Expand Down Expand Up @@ -225,3 +295,92 @@ fn add_tail_call_c<I: Input, C: Ctrl<I>>(
iter.collect()
}
}

/// Add a call to any accepting state.
#[inline]
#[must_use]
fn add_call_state<I: Input, C: Ctrl<I>>(
s: State<I, C>,
other_init: &BTreeSet<usize>,
accepting_indices: &BTreeSet<usize>,
) -> State<I, BTreeSet<usize>> {
State {
transitions: add_call_curry(s.transitions, other_init, accepting_indices),
non_accepting: s.non_accepting,
}
}

/// Add a call to any accepting state.
#[inline]
#[must_use]
fn add_call_curry<I: Input, C: Ctrl<I>>(
s: Curry<I, C>,
other_init: &BTreeSet<usize>,
accepting_indices: &BTreeSet<usize>,
) -> Curry<I, BTreeSet<usize>> {
match s {
Curry::Wildcard(t) => {
Curry::Wildcard(add_call_transition(t, other_init, accepting_indices))
}
Curry::Scrutinize { filter, fallback } => Curry::Scrutinize {
filter: add_call_range_map(filter, other_init, accepting_indices),
fallback: fallback.map(|f| add_call_transition(f, other_init, accepting_indices)),
},
}
}

/// Add a call to any accepting state.
#[inline]
#[must_use]
fn add_call_range_map<I: Input, C: Ctrl<I>>(
s: RangeMap<I, C>,
other_init: &BTreeSet<usize>,
accepting_indices: &BTreeSet<usize>,
) -> RangeMap<I, BTreeSet<usize>> {
RangeMap(
s.0.into_iter()
.map(|(k, v)| (k, add_call_transition(v, other_init, accepting_indices)))
.collect(),
)
}

/// Add a call to any accepting state.
#[inline]
#[must_use]
#[allow(clippy::todo)] // <-- FIXME
fn add_call_transition<I: Input, C: Ctrl<I>>(
s: Transition<I, C>,
_other_init: &BTreeSet<usize>,
accepting_indices: &BTreeSet<usize>,
) -> Transition<I, BTreeSet<usize>> {
match s {
Transition::Lateral { dst, update } => {
if dst.view().any(|ref i| accepting_indices.contains(i)) {
todo!()
} else {
Transition::Lateral {
dst: dst.view().collect(),
update,
}
}
}
Transition::Call {
region,
detour,
dst,
combine,
} => {
if dst.view().any(|ref i| accepting_indices.contains(i)) {
todo!()
} else {
Transition::Call {
region,
detour: detour.view().collect(),
dst: dst.view().collect(),
combine,
}
}
}
Transition::Return { region } => Transition::Return { region },
}
}

0 comments on commit bccdc5b

Please sign in to comment.