tamer: parse::state: Further generalize ParseState::delegate

This moves enough of the handling of complex type conversions into the
various components of `TransitionResult` (and itself), which simplifies
delegation and opens up the possibility of having specialized
delegation/stitching methods implemented atop of `TransitionResult`.

DEV-13156
main
Mike Gerwitz 2022-11-16 10:34:04 -05:00
parent a17e53258b
commit 60ce1305cc
2 changed files with 136 additions and 58 deletions

View File

@ -68,6 +68,23 @@ impl<S: ParseState> ParseStatus<S> {
Object(obj) => Object(obj),
}
}
/// Transform into a [`ParseStatus`] for a [`ParseState`] `SB`.
///
/// This transforms [`ParseStatus::Object`] inner value using [`Into`].
pub fn inner_into<SB: ParseState>(
self,
) -> ParseStatus<<SB as ParseState>::Super>
where
S: StitchableParseState<SB>,
{
use ParseStatus::*;
match self {
Incomplete => Incomplete,
Object(obj) => Object(obj.into()),
}
}
}
impl<S: ParseState<Object = T>, T: Object> From<T> for ParseStatus<S> {
@ -285,12 +302,7 @@ where
Self: StitchableParseState<SP>,
C: AsMut<<Self as ParseState>::Context>,
{
use ParseStatus::{Incomplete, Object as Obj};
let TransitionResult(Transition(newst), data) =
self.parse_token(tok, context.as_mut());
match data {
self.parse_token(tok, context.as_mut()).branch_dead(
// The token of lookahead must bubble up to the ancestor
// [`Parser`] so that it knows to provide that token in place
// of the next from the token stream,
@ -299,13 +311,9 @@ where
// the dead state simply means that we should transition back
// out of this parser back to `SP` so that it can use the
// token of lookahead.
TransitionData::Dead(Lookahead(lookahead)) => {
dead().incomplete().with_lookahead(lookahead)
}
TransitionData::Result(..) => {
TransitionResult(into(newst).into_super(), data.inner_into())
}
}
|_| dead().incomplete(),
|st, result| TransitionData::from(result).transition(into(st)),
)
}
/// Delegate parsing of a token from our superstate
@ -328,19 +336,11 @@ where
where
C: AsMut<<Self as ParseState>::Context>,
{
let TransitionResult(Transition(newst), data) =
self.parse_token(tok, context.as_mut());
match data {
TransitionData::Dead(Lookahead(lookahead)) => {
dead(newst, lookahead, context)
}
// Since this is child state,
// [`TransitionResult`] has already converted into the
// superstate for us.
_ => TransitionResult(Transition(newst), data),
}
self.parse_token(tok, context.as_mut())
.branch_dead_la::<Self::Super>(
|st, Lookahead(la)| dead(st, la, context),
|st, result, la| result.transition(st).maybe_with_lookahead(la),
)
}
/// Delegate parsing from a compatible, stitched [`ParseState`] `SP`
@ -369,35 +369,19 @@ where
Self: PartiallyStitchableParseState<SP>,
C: AsMut<<Self as ParseState>::Context>,
{
use ParseStatus::{Incomplete, Object as Obj};
use ParseStatus::{Incomplete, Object};
let TransitionResult(Transition(newst), data) =
self.parse_token(tok, context.as_mut());
match data {
TransitionData::Dead(Lookahead(lookahead)) => {
dead().incomplete().with_lookahead(lookahead)
}
TransitionData::Result(Ok(Obj(obj)), lookahead) => {
// TODO: check accepting
objf(newst, obj).maybe_with_lookahead(lookahead)
}
TransitionData::Result(result, lookahead) => TransitionResult(
into(newst).into_super(),
TransitionData::Result(
match result {
Ok(_) => Ok(Incomplete),
// First convert the error into `SP::Error`,
// and then `SP::Super::Error`
// (which will be the same type if SP is closed).
Err(e) => Err(e.into().into()),
},
lookahead,
),
),
}
self.parse_token(tok, context.as_mut()).branch_dead(
|_| dead().incomplete(),
|st, result| match result {
Ok(Object(obj)) => {
// TODO: check accepting
objf(st, obj)
}
Ok(Incomplete) => into(st).incomplete(),
Err(e) => into(st).err(e),
},
)
}
/// Delegate parsing from a compatible, stitched [`ParseState`] `SP`

View File

@ -21,7 +21,7 @@
use super::{
ClosedParseState, ParseState, ParseStateResult, ParseStatus,
StitchableParseState, Token,
PartiallyStitchableParseState, StitchableParseState, Token,
};
use std::{
convert::Infallible,
@ -141,6 +141,77 @@ impl<S: ParseState> TransitionResult<S> {
}
}
}
/// Conditionally map to a [`TransitionResult`] based on whether the
/// inner [`TransitionData`] represents a dead state transition
/// ([`TransitionData::Dead`]).
///
/// Inner values are unwrapped before applying one of `fdead` or
/// `falive`.
///
/// Lookahead is automatically propagated to the resulting
/// [`TransitionResult`],
/// ensuring that the token cannot be lost.
/// Consequently,
/// it is important that the [`TransitionResult`] returned by `fdead`
/// or `falive` _does not contain a token of lookahead_,
/// otherwise the system will panic,
/// since two tokens of lookahead cannot be accommodated.
/// This is not as bad as it sounds in practice,
/// since no token of input is provided to either of the branches,
/// and so would have to be manufactured by
/// (or have been previously stored by)
/// a calling parser.
pub fn branch_dead<SB: ParseState>(
self,
fdead: impl FnOnce(S) -> TransitionResult<<SB as ParseState>::Super>,
falive: impl FnOnce(
S,
ParseStateResult<S>,
) -> TransitionResult<<SB as ParseState>::Super>,
) -> TransitionResult<<SB as ParseState>::Super>
where
S: PartiallyStitchableParseState<SB>,
{
self.branch_dead_la(
|st, Lookahead(la)| fdead(st).with_lookahead(la),
|st, result, la| falive(st, result).maybe_with_lookahead(la),
)
}
/// Conditionally map to a [`TransitionResult`] based on whether the
/// inner [`TransitionData`] represents a dead state transition
/// ([`TransitionData::Dead`]).
///
/// This is like [`Self::branch_dead`],
/// but exposes the token of lookahead (if any) and therefore _puts
/// the onus on the caller to ensure that the token is not lost_.
/// As such,
/// this method is private to the `parse` module.
pub(in super::super) fn branch_dead_la<SB: ParseState>(
self,
fdead: impl FnOnce(
S,
Lookahead<<S as ParseState>::Token>,
) -> TransitionResult<<SB as ParseState>::Super>,
falive: impl FnOnce(
S,
ParseStateResult<S>,
Option<Lookahead<<S as ParseState>::Token>>,
) -> TransitionResult<<SB as ParseState>::Super>,
) -> TransitionResult<<SB as ParseState>::Super>
where
S: PartiallyStitchableParseState<SB>,
{
use TransitionData::{Dead, Result};
let Self(Transition(st), data) = self;
match data {
Dead(la) => fdead(st, la),
Result(result, la) => falive(st, result, la),
}
}
}
/// Token to use as a lookahead token in place of the next token from the
@ -198,6 +269,19 @@ impl<S: ParseState> TransitionData<S> {
}
}
/// Associate this [`TransitionData`] with a state transition for a
/// [`ParseState`] `SB`,
/// translating from `S` if necessary.
pub fn transition<SB: ParseState>(
self,
to: impl Into<Transition<SB>>,
) -> TransitionResult<<SB as ParseState>::Super>
where
S: StitchableParseState<SB>,
{
TransitionResult(to.into().into_super(), self.inner_into())
}
/// Reference to the token of lookahead,
/// if any.
pub(in super::super) fn lookahead_ref(
@ -316,15 +400,13 @@ impl<S: ParseState> TransitionData<S> {
where
S: StitchableParseState<SB>,
{
use ParseStatus::{Incomplete, Object};
use TransitionData::*;
match self {
Dead(la) => Dead(la),
Result(result, la) => Result(
match result {
Ok(Incomplete) => Ok(Incomplete),
Ok(Object(obj)) => Ok(Object(obj.into())),
Ok(status) => Ok(status.inner_into()),
// First convert the error into `SB::Error`,
// and then `SP::Super::Error`
// (which will be the same type if SB is closed).
@ -336,6 +418,12 @@ impl<S: ParseState> TransitionData<S> {
}
}
impl<S: ParseState> From<ParseStateResult<S>> for TransitionData<S> {
fn from(result: ParseStateResult<S>) -> Self {
Self::Result(result, None)
}
}
/// A verb denoting a state transition.
///
/// This is typically instantiated directly by a [`ParseState`] to perform a
@ -473,6 +561,12 @@ impl<S: ParseState> Transition<S> {
}
}
impl<S: ParseState> From<S> for Transition<S> {
fn from(st: S) -> Self {
Self(st)
}
}
impl<S: ClosedParseState> FromResidual<(Transition<S>, ParseStateResult<S>)>
for TransitionResult<S>
{