From 60ce1305cca1f0dfaf2af55ceb4ab63b91586795 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Wed, 16 Nov 2022 10:34:04 -0500 Subject: [PATCH] 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 --- tamer/src/parse/state.rs | 92 +++++++++++-------------- tamer/src/parse/state/transition.rs | 102 ++++++++++++++++++++++++++-- 2 files changed, 136 insertions(+), 58 deletions(-) diff --git a/tamer/src/parse/state.rs b/tamer/src/parse/state.rs index e6668d7c..17676f9a 100644 --- a/tamer/src/parse/state.rs +++ b/tamer/src/parse/state.rs @@ -68,6 +68,23 @@ impl ParseStatus { Object(obj) => Object(obj), } } + + /// Transform into a [`ParseStatus`] for a [`ParseState`] `SB`. + /// + /// This transforms [`ParseStatus::Object`] inner value using [`Into`]. + pub fn inner_into( + self, + ) -> ParseStatus<::Super> + where + S: StitchableParseState, + { + use ParseStatus::*; + + match self { + Incomplete => Incomplete, + Object(obj) => Object(obj.into()), + } + } } impl, T: Object> From for ParseStatus { @@ -285,12 +302,7 @@ where Self: StitchableParseState, C: AsMut<::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<::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::( + |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, C: AsMut<::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` diff --git a/tamer/src/parse/state/transition.rs b/tamer/src/parse/state/transition.rs index b8950dca..e9240f22 100644 --- a/tamer/src/parse/state/transition.rs +++ b/tamer/src/parse/state/transition.rs @@ -21,7 +21,7 @@ use super::{ ClosedParseState, ParseState, ParseStateResult, ParseStatus, - StitchableParseState, Token, + PartiallyStitchableParseState, StitchableParseState, Token, }; use std::{ convert::Infallible, @@ -141,6 +141,77 @@ impl TransitionResult { } } } + + /// 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( + self, + fdead: impl FnOnce(S) -> TransitionResult<::Super>, + falive: impl FnOnce( + S, + ParseStateResult, + ) -> TransitionResult<::Super>, + ) -> TransitionResult<::Super> + where + S: PartiallyStitchableParseState, + { + 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( + self, + fdead: impl FnOnce( + S, + Lookahead<::Token>, + ) -> TransitionResult<::Super>, + falive: impl FnOnce( + S, + ParseStateResult, + Option::Token>>, + ) -> TransitionResult<::Super>, + ) -> TransitionResult<::Super> + where + S: PartiallyStitchableParseState, + { + 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 TransitionData { } } + /// Associate this [`TransitionData`] with a state transition for a + /// [`ParseState`] `SB`, + /// translating from `S` if necessary. + pub fn transition( + self, + to: impl Into>, + ) -> TransitionResult<::Super> + where + S: StitchableParseState, + { + 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 TransitionData { where S: StitchableParseState, { - 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 TransitionData { } } +impl From> for TransitionData { + fn from(result: ParseStateResult) -> 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 Transition { } } +impl From for Transition { + fn from(st: S) -> Self { + Self(st) + } +} + impl FromResidual<(Transition, ParseStateResult)> for TransitionResult {