parse::state::transition::TransitionResult::branch_dead: Add branch context

This works around limitations of Rust's borrow checker as of the time of
writing.  See the provided documentation for more information.

The branch context is not yet exposed to the `delegate` family of methods;
it will be added only as needed in the future.

DEV-13708
main
Mike Gerwitz 2023-03-07 10:44:28 -05:00
parent d99a8efbaf
commit 25c0aa180e
3 changed files with 49 additions and 19 deletions

View File

@ -479,12 +479,12 @@ impl AirAggregate {
let tok = etok.into();
let tokspan = tok.span();
expr.parse_token(tok, asg).branch_dead::<Self>(
expr.parse_token(tok, asg).branch_dead::<Self, _>(
// TODO: Enforce using type system to avoid need for this
// runtime check and prove that it is indeed impossible
// (which otherwise could fail to be the case due to changes
// since this was written).
|_| {
|_, ()| {
diagnostic_unreachable!(
vec![tokspan.internal_error(
"unexpected dead state transition at this token"
@ -492,11 +492,12 @@ impl AirAggregate {
"AirExprAggregate should not have dead states"
)
},
|expr, result| {
|expr, result, ()| {
result
.map(ParseStatus::reflexivity)
.transition(Self::PkgExpr(oi_pkg, expr))
},
(),
)
}
}

View File

@ -330,8 +330,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.
|_| dead().incomplete(),
|st, result| TransitionData::from(result).transition(into(st)),
|_, ()| dead().incomplete(),
|st, result, ()| TransitionData::from(result).transition(into(st)),
(),
)
}
@ -356,9 +357,12 @@ where
C: AsMut<<Self as ParseState>::Context>,
{
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),
.branch_dead_la::<Self::Super, _>(
|st, Lookahead(la), ()| dead(st, la, context),
|st, result, la, ()| {
result.transition(st).maybe_with_lookahead(la)
},
(),
)
}
@ -391,8 +395,8 @@ where
use ParseStatus::{Incomplete, Object};
self.parse_token(tok, context.as_mut()).branch_dead(
|_| dead().incomplete(),
|st, result| match result {
|_, ()| dead().incomplete(),
|st, result, ()| match result {
Ok(Object(obj)) => {
// TODO: check accepting
objf(st, obj)
@ -400,6 +404,7 @@ where
Ok(Incomplete) => into(st).incomplete(),
Err(e) => into(st).err(e),
},
(),
)
}

View File

@ -178,25 +178,43 @@ impl<S: ParseState> TransitionResult<S> {
/// and so would have to be manufactured by
/// (or have been previously stored by)
/// a calling parser.
pub fn branch_dead<SB: ParseState>(
///
/// Ownership and Branching
/// =======================
/// At the time of writing (2023),
/// Rust's borrow checker cannot understand that the arguments to
/// `fdead` and `falive` are utilized in exclusive branches;
/// the borrowing happens at the call to `branch_dead` itself.
/// The causes ownership problems when both branches want to utilize the
/// same data.
///
/// To work around this limitation,
/// this method accepts an arbitrary branching context `bctx` that
/// will be passed to either `fdead` or `falive`;
/// this can be utilized in place of closure.
pub fn branch_dead<SB: ParseState, C>(
self,
fdead: impl FnOnce(S) -> TransitionResult<<SB as ParseState>::Super>,
fdead: impl FnOnce(S, C) -> TransitionResult<<SB as ParseState>::Super>,
falive: impl FnOnce(
S,
ParseStateResult<S>,
C,
) -> TransitionResult<<SB as ParseState>::Super>,
bctx: C,
) -> TransitionResult<<SB as ParseState>::Super>
where
S: PartiallyStitchableParseState<SB>,
{
self.branch_dead_la(
|st, Lookahead(la)| {
fdead(st).with_lookahead(<SB as ParseState>::Token::from(la))
|st, Lookahead(la), bctx| {
fdead(st, bctx)
.with_lookahead(<SB as ParseState>::Token::from(la))
},
|st, result, la| {
falive(st, result)
|st, result, la, bctx| {
falive(st, result, bctx)
.maybe_with_lookahead(la.map(Lookahead::inner_into))
},
bctx,
)
}
@ -209,17 +227,23 @@ impl<S: ParseState> TransitionResult<S> {
/// 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>(
///
/// For information about the branch context `bctx`,
/// see the public-facing method [`Self::branch_dead`].
pub(in super::super) fn branch_dead_la<SB: ParseState, C>(
self,
fdead: impl FnOnce(
S,
Lookahead<<S as ParseState>::Token>,
C,
) -> TransitionResult<<SB as ParseState>::Super>,
falive: impl FnOnce(
S,
ParseStateResult<S>,
Option<Lookahead<<S as ParseState>::Token>>,
C,
) -> TransitionResult<<SB as ParseState>::Super>,
bctx: C,
) -> TransitionResult<<SB as ParseState>::Super>
where
S: PartiallyStitchableParseState<SB>,
@ -229,8 +253,8 @@ impl<S: ParseState> TransitionResult<S> {
let Self(Transition(st), data) = self;
match data {
Dead(la) => fdead(st, la),
Result(result, la) => falive(st, result, la),
Dead(la) => fdead(st, la, bctx),
Result(result, la) => falive(st, result, la, bctx),
}
}