tamer: parse::util (ExpandableParseState, ExpandableInto): Clarifying traits

These traits serve to abstract away some of the type-level details and
clearly state what the end result is (something stitchable with a parent).

I'm admittedly battling myself on this concept a bit.  The proper layer of
abstraction is the concept of expansion, which is an abstraction that is
likely to be maintained all the way through, but we strip the abstraction
for the sake of delegation.  Maybe the better option is to provide a
different method of delegation and avoid the stripping at all, and avoid the
awkward interaction with the dead state.

The awkwardness comes from the fact that delegating right now is so rigid
and defined in terms of a method on state rather than a mapping between
`TransitionResult`s.  But I really need to move on... ;_;

The original design was trying to generalize this such that composition at
the attribute parser level (for NIR) would be able to just accept any
sitchable parser with the convention that the dead state is the replacement
token.  But that is the wrong layer of abstraction, which not only makes it
confusing, but is asking for trouble when someone inevitably violates that
contract.

With all of that said, `StitchableExpansionState` _is_ a delegation.  It
could just as easily be a function (`is_accepting` always delegates too), so
perhaps that should just be generalized as reifying delegation as a
`ParseState`.

DEV-13156
main
Mike Gerwitz 2022-11-15 12:21:49 -05:00
parent 03cf652c41
commit ddb4f24ea5
2 changed files with 70 additions and 3 deletions

View File

@ -29,7 +29,7 @@ use crate::{diagnose::Annotate, diagnostic_panic, span::Span, sym::SymbolId};
use super::{
prelude::*,
state::{Lookahead, TransitionData},
state::{Lookahead, StitchableParseState, TransitionData},
};
use std::{fmt::Display, marker::PhantomData};
@ -52,6 +52,27 @@ pub enum Expansion<T, O: Object> {
impl<T: Token, O: Object> Object for Expansion<T, O> {}
/// A [`ClosedParseState`] that is able to serve as an expansion parser.
///
/// An expansion parser is a parser yielding [`Expansion`],
/// intended to be integrated into another token stream.
pub trait ExpandableParseState<O: Object> = ClosedParseState
where
O: Token + Eq,
Self: ParseState<Object = Expansion<<Self as ParseState>::Token, O>>;
/// An [`ExpandableParseState`] capable of expanding into the token stream
/// of a parent [`ParseState`] `SP`.
///
/// This trait asserts that an [`ExpandableParseState`] is a
/// [`StitchableParseState<SP>`](StitchableParseState) after being wrapped
/// by [`StitchableExpansionState`].
pub trait ExpandableInto<SP: ParseState> =
ExpandableParseState<<SP as ParseState>::Object>
where
StitchableExpansionState<Self, <SP as ParseState>::Object>:
StitchableParseState<SP>;
/// Convert a [`ClosedParseState`] yielding an [`Expansion<T,O>`](Expansion)
/// object into a parser yielding `O` with a dead state yielding `T`.
///
@ -91,8 +112,7 @@ where
impl<S: ClosedParseState, O: Object> ParseState
for StitchableExpansionState<S, O>
where
O: Token + Eq,
S: ParseState<Object = Expansion<<S as ParseState>::Token, O>>,
S: ExpandableParseState<O>,
{
type Token = S::Token;
type Object = O;

View File

@ -141,3 +141,50 @@ fn expansion_stripping_panics_if_lookahead() {
// The above token will trigger the panic on the first call.
let _ = ExpansionSut::parse(toks.into_iter()).next();
}
// This test would fail at compile-time.
#[test]
fn expandable_into_is_stitchable_with_target() {
// This is utilized only for its types in the below assertions.
#[derive(Debug, PartialEq, Eq)]
struct TargetParseState;
impl ParseState for TargetParseState {
type Token = SPair;
type Object = TestObject;
type Error = Infallible;
fn parse_token(
self,
_tok: Self::Token,
_ctx: &mut Self::Context,
) -> TransitionResult<Self::Super> {
unimplemented!()
}
fn is_accepting(&self, _ctx: &Self::Context) -> bool {
unimplemented!()
}
}
impl Display for TargetParseState {
fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
unimplemented!()
}
}
// Asserts that the wrapping `StitchableParseState` has transformed the
// `TestParseState` into something stitchable.
//
// This serves as a sanity check for the below.
assert_impl_all!(ExpansionSut: StitchableParseState<TargetParseState>);
// The `ExpandableInto` trait alias is responsible for asserting that a
// given parser is an expansion parser that is able to be converted
// into a parser stitchable with the target.
//
// If this fails but the above assertion succeeds,
// then the compatibility is working but something is wrong with the
// definition of `ExpandableInto`.
assert_impl_all!(TestParseState: ExpandableInto<TargetParseState>);
}