Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inductive call: another Transition instead of forcing a lateral move #57

Merged
merged 4 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn state_0<I: Iterator<Item = (usize, char)>>(
'('..='(' => {
let detour = state_0(input, (), Some(("parentheses", index)))?;
let postprocessed = (|(), ()| ())(acc, detour);
state_0(input, postprocessed, stack_top)
state_0(input, acc, stack_top)
}
')'..=')' => match stack_top {
Some((region, _)) if region == "parentheses" => Ok(acc),
Expand Down
10 changes: 9 additions & 1 deletion automata/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,15 @@ impl<I: Input, C: Ctrl<I>> Check<I, C> for Transition<I, C> {
#[inline]
fn check(&self, n_states: NonZeroUsize) -> Result<(), IllFormed<I, C>> {
match *self {
Self::Lateral { ref dst, .. } | Self::Call { ref dst, .. } => dst.check(n_states),
Self::Lateral { ref dst, .. } => dst.check(n_states),
Self::Call {
ref detour,
ref dst,
..
} => {
detour.check(n_states)?;
dst.check(n_states)
}
Self::Return { .. } => Ok(()),
}
}
Expand Down
8 changes: 6 additions & 2 deletions automata/src/combinators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,16 @@ fn add_tail_call_transition<I: Input, C: Ctrl<I>>(
Transition::Call {
region,
ref detour,
ref dst,
dst,
combine,
} => Transition::Call {
region,
detour: add_tail_call_c(detour, other_init, accepting_indices),
dst: add_tail_call_c(dst, other_init, accepting_indices),
dst: Box::new(add_tail_call_transition(
*dst,
other_init,
accepting_indices,
)),
combine,
},
Transition::Return { region } => Transition::Return { region },
Expand Down
2 changes: 1 addition & 1 deletion automata/src/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl<I: Input, C: Ctrl<I>> Transition<I, C> {
} => Transition::Call {
region,
detour: detour.view().collect(),
dst: dst.view().collect(),
dst: Box::new(dst.generalize()),
combine,
},
Self::Return { region } => Transition::Return { region },
Expand Down
6 changes: 3 additions & 3 deletions automata/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,13 +382,13 @@ fn fix_indices_transition<I: Input, C: Ctrl<I>>(
},
Transition::Call {
region,
detour,
ref detour,
dst,
combine,
} => Transition::Call {
region,
detour: unwrap!(ordering.binary_search(&detour)),
dst: unwrap!(ordering.binary_search(&dst)),
detour: unwrap!(ordering.binary_search(detour)),
dst: Box::new(fix_indices_transition(*dst, ordering)),
combine,
},
Transition::Return { region } => Transition::Return { region },
Expand Down
6 changes: 3 additions & 3 deletions automata/src/in_progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

//! Execute an automaton on an input sequence.

use crate::{try_merge, Ctrl, Graph, IllFormed, Input, ToSrc};
use crate::{try_merge, Ctrl, Graph, IllFormed, Input, ToSrc, Transition};
use core::fmt;

/// Execute an automaton on an input sequence.
Expand All @@ -17,7 +17,7 @@ pub struct InProgress<'graph, I: Input, C: Ctrl<I>, In: Iterator<Item = I>> {
/// Iterator over input tokens.
pub input: In,
/// Internal stack.
pub stack: Vec<C>,
pub stack: Vec<Transition<I, C>>,
/// Internal state.
pub ctrl: C,
/// Output type as we go.
Expand Down Expand Up @@ -90,7 +90,7 @@ fn step<I: Input, C: Ctrl<I>>(
graph: &Graph<I, C>,
ctrl: &C,
maybe_token: Option<I>,
stack: &mut Vec<C>,
stack: &mut Vec<Transition<I, C>>,
output_t: &str,
) -> Result<(Option<C>, String), ParseError<I, C>> {
ctrl.view().try_fold((), |(), i| {
Expand Down
10 changes: 8 additions & 2 deletions automata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,10 @@ pub fn dyck_d() -> Deterministic<char> {
Transition::Call {
region: "parentheses",
detour: 0,
dst: 0,
dst: Box::new(Transition::Lateral {
dst: 0,
update: None,
}),
combine: ff!(|(), ()| ()),
},
),
Expand Down Expand Up @@ -269,7 +272,10 @@ pub fn dyck_nd() -> Nondeterministic<char> {
Transition::Call {
region: "parentheses",
detour: iter::once(0).collect(),
dst: iter::once(0).collect(),
dst: Box::new(Transition::Lateral {
dst: iter::once(0).collect(),
update: None,
}),
combine: ff!(|(), ()| ()),
},
),
Expand Down
2 changes: 1 addition & 1 deletion automata/src/map_indices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl<I: Input, C: Ctrl<I>> Transition<I, C> {
} => Self::Call {
region,
detour: detour.map_indices(&mut f),
dst: dst.map_indices(f),
dst: Box::new(dst.map_indices(f)),
combine,
},
Self::Return { region } => Self::Return { region },
Expand Down
4 changes: 1 addition & 3 deletions automata/src/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,7 @@ impl<I: Input, C: Ctrl<I>> Merge for Transition<I, C> {
detour: l_detour
.merge(r_detour)
.map_err(|(a, b)| IllFormed::Superposition(a, b))?,
dst: l_dst
.merge(r_dst)
.map_err(|(a, b)| IllFormed::Superposition(a, b))?,
dst: Box::new(l_dst.merge(*r_dst)?),
combine: l_combine.merge(r_combine).map_err(|(a, b)| {
IllFormed::IncompatibleCombinators(Box::new(a), Box::new(b))
})?,
Expand Down
48 changes: 22 additions & 26 deletions automata/src/qc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,37 +152,33 @@ shrink_only!(|self: &Curry| match *self {

shrink_only!(|self: &Transition| {
#[allow(clippy::shadow_unrelated, unreachable_code, unused_variables)]
match self.clone() {
Self::Return { region: _ } => Box::new(iter::empty()),
Self::Lateral { dst, update } => Box::new(
match *self {
Self::Return { .. } => Box::new(iter::empty()),
Self::Lateral {
ref dst,
ref update,
} => Box::new(
iter::once(Self::Return { region: "region" }).chain(
(dst, update)
(dst.clone(), update.clone())
.shrink()
.map(|(dst, update)| Self::Lateral { dst, update }),
),
),
Self::Call {
region: _,
detour,
dst,
combine,
} => {
Box::new(
Self::Lateral {
dst: dst.clone(),
update: None,
}
.shrink()
.chain((detour, dst, combine).shrink().map(
|(detour, dst, combine)| Self::Call {
region: "region",
detour,
dst,
combine,
},
)),
)
}
ref detour,
ref dst,
ref combine,
..
} => Box::new(dst.as_ref().shrink().chain(
(detour.clone(), dst.clone(), combine.clone()).shrink().map(
|(detour, dst, combine)| Self::Call {
region: "region",
detour,
dst,
combine,
},
),
)),
}
});

Expand Down Expand Up @@ -255,7 +251,7 @@ impl<C: Ctrl<u8>> Transition<u8, C> {
|n, r| Self::Call {
region: "region",
detour: C::arbitrary_given(n, r),
dst: C::arbitrary_given(n, r),
dst: Box::new(Transition::arbitrary_given(n, r)),
combine: Arbitrary::arbitrary(r),
},
|_, _| Self::Return { region: "region" },
Expand Down
2 changes: 1 addition & 1 deletion automata/src/reindex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl<I: Input, C: Ctrl<I>> Transition<I, C> {
} => Self::Call {
region,
detour: detour.clone().map_indices(update_fn),
dst: dst.clone().map_indices(update_fn),
dst: Box::new(dst.clone().map_indices(update_fn)),
combine: combine.clone(),
},
Self::Return { region } => Self::Return { region },
Expand Down
21 changes: 19 additions & 2 deletions automata/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,17 @@ mod prop {
};
star.accept(input).is_ok()
}

// TODO:
/*
fn star_star_identity(d: Deterministic<u8>, input: Vec<u8>) -> bool {
let Ok(once) = panic::catch_unwind(|| d.star()) else {
return true;
};
let twice = once.clone().star();
once.accept(input.iter().copied()) == twice.accept(input)
}
*/
}
}

Expand Down Expand Up @@ -506,7 +517,10 @@ mod reduced {
transitions: Curry::Wildcard(Transition::Call {
region: "region",
detour: 0,
dst: 1,
dst: Box::new(Transition::Lateral {
dst: 1,
update: None,
}),
combine: FF {
src: "|(), ()| ()".to_owned(),
lhs_t: "()".to_owned(),
Expand Down Expand Up @@ -662,7 +676,10 @@ mod reduced {
transitions: Curry::Wildcard(Transition::Call {
region: "region",
detour: 0,
dst: 0,
dst: Box::new(Transition::Lateral {
dst: 0,
update: None,
}),
combine: ff!(|(), ()| ()),
}),
non_accepting: BTreeSet::new(),
Expand Down
16 changes: 10 additions & 6 deletions automata/src/to_src.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ impl<I: Input> Curry<I, usize> {
match *self {
Self::Wildcard(ref etc) => format!(
r#"
_ => {},"#,
_ => {{
{}
}}"#,
etc.to_src(),
),
Self::Scrutinize {
Expand All @@ -318,7 +320,9 @@ impl<I: Input> RangeMap<I, usize> {
self.0.iter().fold(String::new(), |acc, (k, v)| {
format!(
r#"{acc}
{} => {},"#,
{} => {{
{}
}},"#,
k.to_src(),
v.to_src(),
)
Expand All @@ -342,15 +346,15 @@ impl<I: Input> Transition<I, usize> {
Self::Call {
region,
detour,
dst,
ref dst,
combine: FF { ref src, .. },
} => format!(
r#"{{
"\
let detour = state_{detour}(input, (), Some(({}, index)))?;
let postprocessed = ({src})(acc, detour);
state_{dst}(input, postprocessed, stack_top)
}}"#,
{}",
region.to_src(),
dst.to_src(),
),
Self::Return { region } => {
format!(
Expand Down
26 changes: 15 additions & 11 deletions automata/src/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! Transition in an automaton: an action and a destination state.

use crate::{Ctrl, Input, InputError, Merge, ParseError, Update, FF};
use core::{cmp, mem};
use core::{cmp, iter, mem};
use std::collections::BTreeSet;

// TODO: rename `Call` to `Open` and `Return` to `Close`
Expand All @@ -30,7 +30,7 @@ pub enum Transition<I: Input, C: Ctrl<I>> {
/// Call (and require a successful run from) this state before continuing.
detour: C,
/// After the call has succeeded, go to this state.
dst: C,
dst: Box<Self>,
/// Combine the cached results and the results of the called parser with this function.
combine: FF,
},
Expand Down Expand Up @@ -137,7 +137,7 @@ impl<I: Input, C: Ctrl<I>> Transition<I, C> {
pub fn invoke(
&self,
output_t: &str,
stack: &mut Vec<C>,
stack: &mut Vec<Transition<I, C>>,
) -> Result<Option<(C, String)>, ParseError<I, C>> {
match *self {
Self::Lateral {
Expand All @@ -156,14 +156,17 @@ impl<I: Input, C: Ctrl<I>> Transition<I, C> {
ref dst,
..
} => {
stack.push(dst.clone());
stack.push(*dst.clone());
Ok(Some((detour.clone(), "()".to_owned())))
}
Self::Return { .. } => {
let rtn_to = stack
.pop()
.ok_or(ParseError::BadInput(InputError::Unopened))?;
Ok(Some((rtn_to, output_t.to_owned())))
// Ok(Some((rtn_to, output_t.to_owned())))
// No longer strictly small-step semantics,
// but the alternative is a nightmare
rtn_to.invoke(output_t, stack)
}
}
}
Expand All @@ -185,15 +188,15 @@ impl<I: Input, C: Ctrl<I>> Transition<I, C> {
/// For returns, it's nothing.
#[inline]
#[must_use]
pub fn dsts(&self) -> Vec<&C> {
pub fn dsts(&self) -> BTreeSet<&C> {
match *self {
Self::Lateral { ref dst, .. } => vec![dst],
Self::Lateral { ref dst, .. } => iter::once(dst).collect(),
Self::Call {
ref detour,
ref dst,
..
} => vec![detour, dst],
Self::Return { .. } => vec![],
} => iter::once(detour).chain(dst.dsts()).collect(),
Self::Return { .. } => BTreeSet::new(),
}
}

Expand Down Expand Up @@ -227,7 +230,7 @@ impl<I: Input> Transition<I, usize> {
} => Transition::Call {
region,
detour: C::from_usize(detour),
dst: C::from_usize(dst),
dst: Box::new(dst.convert_ctrl()),
combine,
},
Self::Return { region } => Transition::Return { region },
Expand All @@ -241,11 +244,12 @@ impl<I: Input> Transition<I, BTreeSet<usize>> {
#[allow(clippy::missing_panics_doc)]
pub fn star(&mut self, init: &BTreeSet<usize>, accepting: &BTreeSet<usize>) {
match *self {
Transition::Lateral { ref mut dst, .. } | Transition::Call { ref mut dst, .. } => {
Transition::Lateral { ref mut dst, .. } => {
if dst.iter().any(|i| accepting.contains(i)) {
*dst = unwrap!(mem::take(dst).merge(init.clone()));
}
}
Transition::Call { ref mut dst, .. } => dst.star(init, accepting),
Transition::Return { .. } => {}
}
}
Expand Down
Loading