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-13156main
parent
a17e53258b
commit
60ce1305cc
|
@ -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`
|
||||
|
|
|
@ -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>
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue